2014-11-19 14 views
6

sto dividendo due doppie in .NET e utilizzando l'oggetto risultato di elaborare una data di fine da una data di inizio chiamando (dtstart è predefinito):Perché DateTime.AddDays viene arrotondato al millisecondo più prossimo?

var dValue = 1500.0/8400.0; 
var dtEnd = dtStart.AddDays(dValue); 

Dopo aver ispezionato DTEND ho scoperto che il risultato solo è stato accurato al millisecondo più vicino. Dopo averlo trovato, ho trovato che .AddMilliseconds ecc. Per tutto il numero e TimeSpan.FromDays fa una cosa simile. Mi stavo chiedendo se ci fosse un motivo per cui questo arrotondamento è stato fatto poiché sembra che l'unico modo per ottenere il valore corretto qui sia usare .AddTicks?

Per .AddDays di riferimento chiamate (dove MillisPerDay = 86400000)

public DateTime AddDays(double value) 
{ 
    return Add(value, MillisPerDay); 
} 

che chiama

private DateTime Add(double value, int scale) 
{ 
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5)); 
    if (millis <= -MaxMillis || millis >= MaxMillis) 
    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue")); 
    return AddTicks(millis * TicksPerMillisecond); 
} 
+0

Questo mi è costato un bel po 'di tempo per il debug. Prima di scrivere un codice relativo all'audio ho calcolato che la precisione sia del doppio sia del 'TimeSpan' è sufficiente per identificare univocamente un particolare campione. E poi la stupida funzione 'FromSeconds' ha deciso di arrotondare a millisecondi ... – CodesInChaos

risposta

2

Edit: Dopo averci pensato le cose, ora mi rendo conto la prima versione della mia risposta era sbagliato.

Ecco i commenti nel codice sorgente di Microsoft:

// Returns the DateTime resulting from adding a fractional number of 
// xxxxs to this DateTime. The result is computed by rounding the 
// fractional number of xxxxs given by value to the nearest 
// millisecond, and adding that interval to this DateTime. The 
// value argument is permitted to be negative. 

Questi commenti appaiono su cinque diversi metodi di AddXxxxs (valore doppio), dove xxxx = giorni, ore, millisecondi, minuti e secondi.

Si noti che questo è solo per i metodi che accettano un valore in virgola mobile. (E si può chiedere se sia o meno una buona idea coinvolgere i valori in virgola mobile nei calcoli della data - ma questo è un argomento per un altro giorno.)

Ora, come l'OP sottolinea correttamente, questi cinque metodi tutti chiamano questo metodo:

// Returns the DateTime resulting from adding a fractional number of 
// time units to this DateTime. 
private DateTime Add(double value, int scale) { 
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5)); 
    if (millis <= -MaxMillis || millis >= MaxMillis) 
     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue")); 
    return AddTicks(millis * TicksPerMillisecond); 
} 

Quindi, ciò che viene fatto è che il valore viene aggiunto al DateTime viene arrotondato al numero più vicino di millisecondi prima di essere aggiunto. Ma non il risultato - solo il valore aggiunto (o sottratto).

Questo è effettivamente documentato, ad esempio http://msdn.microsoft.com/en-us/library/system.datetime.adddays%28v=vs.110%29.aspx "Il parametro del valore viene arrotondato al millisecondo più vicino."

Perché non lo so. Forse i programmatori hanno capito che se si utilizzano valori in virgola mobile si dovrebbe essere consapevoli del fatto che i valori in genere non sono completamente accurati. O forse vogliono simulare in qualche modo i tempi in stile Java, che sono basati su millisecondi.

+0

Vorrei che il codificatore originale dell'implementazione DateTime rispondesse. Se non sono stati scalati in millisecondi ma direttamente al numero di tick al giorno, penso che avremmo ottenuto errori di arrotondamento quando aggiungiamo più di 1042,5 giorni. Questo numero deriva dall'intervallo di numeri interi massimo che può essere rappresentato esattamente da un doppio, 2^53 -1, diviso per TicksPerDay. Microsoft usa una risoluzione di 100 ns, quindi TicksPerDay è 24 * 60 * 60 * 1000 * 1000 * 100. Quindi, la mia ipotesi è arrotondare a millisecondi rende il comportamento un po 'più facile da prevedere. –

Problemi correlati