Domanda .NET è l'aritmetica "decimale" indipendente dalla piattaforma / architettura?


Ho chiesto informazioni System.Double recentemente è stato detto che i calcoli possono variare a seconda della piattaforma / architettura. Purtroppo, non riesco a trovare alcuna informazione per dirmi se lo stesso vale per System.Decimal.

Sono sicuro di ottenere Esattamente lo stesso risultato per qualsiasi particolare decimal calcolo indipendente dalla piattaforma / architettura?


16
2018-02-16 15:14


origine


risposte:


Sono sicuro di ottenere esattamente lo stesso risultato per qualsiasi calcolo decimale specifico indipendentemente dalla piattaforma / architettura?

Il C # 4 spec è chiaro che il valore il risultato sarà calcolato lo stesso su qualsiasi piattaforma.

Come nota la risposta di LukeH, la versione ECMA della specifica C # 2 garantisce margini di implementazione conformi per fornire maggiore precisione, quindi un'implementazione di C # 2.0 su un'altra piattaforma potrebbe fornire una risposta più precisa.

Ai fini di questa risposta, discuterò semplicemente del comportamento specificato da C # 4.0.

La specifica C # 4.0 dice:


Il risultato di un'operazione sui valori di tipo decimale è quello che risulterebbe dal calcolo di un risultato esatto (scala di conservazione, come definito per ciascun operatore) e quindi arrotondamento per adattarsi alla rappresentazione. I risultati sono arrotondati al valore rappresentativo più vicino e, quando un risultato è ugualmente vicino a due valori rappresentabili, al valore che ha un numero pari nella posizione della cifra meno significativa [...]. Un risultato zero ha sempre un segno di 0 e una scala di 0.


Poiché il calcolo del valore esatto di un'operazione deve essere lo stesso su qualsiasi piattaforma e l'algoritmo di arrotondamento è ben definito, il valore risultante dovrebbe essere lo stesso indipendentemente dalla piattaforma.

Tuttavia, si noti la parentesi e l'ultima frase sugli zeri. Potrebbe non essere chiaro il motivo per cui tali informazioni siano necessarie.

Una delle stranezze del sistema decimale è che quasi ogni quantità ha più di una possibile rappresentazione. Considera il valore esatto 123.456. Un decimale è la combinazione di un numero intero a 96 bit, un segno a 1 bit e un esponente a otto bit che rappresenta un numero compreso tra -28 e 28. Ciò significa che il valore esatto 123.456 potrebbe essere rappresentato da decimali 123456 x 10-3 o 1234560 x 10-4 o 12345600 x 10-5. La bilancia conta.

La specifica C # impone anche come vengono calcolate le informazioni sulla scala. Il letterale 123.456m sarebbe codificato come 123456 x 10-3e 123.4560m sarebbero codificati come 1234560 x 10-4.

Osserva gli effetti di questa funzione in azione:

decimal d1 = 111.111000m;
decimal d2 = 111.111m;
decimal d3 = d1 + d1;
decimal d4 = d2 + d2;
decimal d5 = d1 + d2;
Console.WriteLine(d1);
Console.WriteLine(d2);
Console.WriteLine(d3);
Console.WriteLine(d4);
Console.WriteLine(d5);
Console.WriteLine(d3 == d4);
Console.WriteLine(d4 == d5);
Console.WriteLine(d5 == d3);

Questo produce

111.111000
111.111
222.222000
222.222
222.222000
True
True
True

Notare come sono le informazioni sulle cifre zero significative conservato tra operazioni su decimali e decimal.ToString lo sa e visualizza gli zeri conservati, se possibile. Si noti anche come l'uguaglianza decimale sa effettuare confronti basati su valori esatti, anche se tali valori hanno rappresentazioni binarie e di stringa diverse.

La specifica che ritengo in realtà non dice che decimal.ToString () ha bisogno di stampare correttamente i valori con zero finale in base alla loro scala, ma sarebbe sciocco da un'implementazione non farlo; Lo considererei un bug.

Ho anche notato che il formato di memoria interna di un decimale nell'implementazione CLR è di 128 bit, suddivisi in: 16 bit inutilizzati, 8 bit di scala, 7 bit inutilizzati, 1 bit di segno e 96 bit di mantissa. Il layout esatto di quei bit in memoria non è definito dalla specifica, e se un'altra implementazione vuole inserire informazioni addizionali in quei 23 bit inutilizzati per i propri scopi, può farlo. Nell'implementazione CLR i bit non utilizzati dovrebbero sempre essere zero.


31
2018-02-16 16:31



Anche se il formato dei tipi in virgola mobile è chiaramente definito, i calcoli in virgola mobile possono infatti avere risultati diversi a seconda dell'architettura, come indicato in sezione 4.1.6 della specifica C #:

Le operazioni in virgola mobile possono essere   eseguito con maggiore precisione di   il tipo di risultato dell'operazione. Per   esempio, alcune architetture hardware   supporta un "esteso" o "lungo doppio"   tipo a virgola mobile con intervallo maggiore   e precisione rispetto al doppio tipo,   e implicitamente eseguono tutto   operazioni a virgola mobile usando questo   tipo più preciso. Solo a   costo eccessivo in termini di prestazioni può tale   architetture hardware da fare   eseguire operazioni in virgola mobile con   meno precisione, e piuttosto che   richiedere un'implementazione per rinunciare   sia prestazioni e precisione, C #   consente di avere un tipo di precisione più elevata   usato per tutti i virgola mobile   operazioni.

Mentre il decimal il tipo è soggetto ad un'approssimazione perché un valore sia rappresentato all'interno del suo intervallo finito, l'intervallo è definito, per definizione, adatto per i calcoli finanziari e monetari. Pertanto, ha una precisione più elevata (e un raggio più piccolo) di float o double. È anche più chiaramente definito rispetto agli altri tipi di virgola mobile, in modo tale che sembrerebbe essere indipendente dalla piattaforma (vedere la sezione 4.1.7 - Sospetto che questa indipendenza dalla piattaforma sia maggiore perché non esiste un supporto hardware standard per i tipi con dimensioni e precisione decimal piuttosto che a causa del tipo stesso, quindi questo potrebbe cambiare con le specifiche future e le architetture hardware).

Se hai bisogno di sapere se un'implementazione specifica di decimal il tipo è corretto, dovresti essere in grado di creare alcuni test unitari usando le specifiche che metteranno alla prova la correttezza.


5
2018-02-16 15:21



Il decimal il tipo è rappresentato in ciò che equivale a base 10 usando una struct (contenente numeri interi, credo), al contrario di double e altri tipi a virgola mobile, che rappresentano valori non integrali in base-2. Perciò, decimals sono esatto rappresentazioni di valori di base 10, entro una precisione standardizzata, su qualsiasi architettura. Questo è vero per qualsiasi architettura che esegue un'implementazione corretta delle specifiche .NET.

Quindi, per rispondere alla tua domanda, dal momento che il comportamento di decimalè standardizzato in questo modo nelle specifiche, decimal i valori dovrebbero essere gli stessi su qualsiasi architettura conforme a quella specifica. Se non sono conformi a tale specifica, allora non sono realmente .NET.

Tipo .NET "decimale" e tipo C / C ++ "float" e "doppio"


5
2018-02-16 15:20



Una lettura delle specifiche suggerisce che decimal -- piace float e double - potrebbe essere consentito un certo margine di manovra nella sua attuazione fintantoché soddisfi determinati standard minimi.

Ecco alcuni estratti dal ECMA C # spec (sezione 11.1.7). Tutta l'enfasi in grassetto è mia.

Il decimal il tipo può rappresentare valori Compreso quelli dentro   la gamma 1 x 10-28 attraverso 1 x 1028 con    almeno 28 cifre significative.

L'insieme finito di valori di tipo decimal sono della forma   (-1)S X c x 10-e, dove il segno S   è 0 o 1, il coefficiente c è dato da 0 <= c < Cmax,   e la scala e è così Emin <= e <= Emax, dove    Cmax è almeno 1 x 1028, Emin  <= 0 e    Emax  > = 28. Il decimal genere non necessariamente   supporta zeri firmati, infiniti o NaN.

Per decimals con un valore assoluto inferiore a 1.0m, il   il valore è esatto almeno il 28esimo decimale   posto. Per decimals con un valore assoluto maggiore di o   uguale a 1.0m, il valore è esatto almeno 28 cifre.

Si noti che la formulazione del Spec. Microsoft C # (sezione 4.1.7) è significativamente diversa da quella delle specifiche ECMA. Sembra bloccare il comportamento di decimal molto più rigorosamente.


5
2018-02-16 16:25