Domanda

Ho un doppio valore f e vorrei un modo per spostarlo leggermente più grande (o più piccolo) per ottenere un nuovo valore che sarà il più vicino possibile rispetto all'originale, ma comunque strettamente maggiore (o inferiore) dell'originale.

Non deve essere vicino all'ultimo bit & # 8212; è più importante che qualsiasi modifica apportata sia garantita per produrre un valore diverso e non tornare indietro all'originale.

È stato utile?

Soluzione

Controlla il tuo file math.h. Se sei fortunato hai il nextafter e nextafterf definiti. Fanno esattamente quello che vuoi in modo portatile e indipendente dalla piattaforma e fanno parte dello standard C99.

Un altro modo per farlo (potrebbe essere una soluzione di fallback) è di scomporre il galleggiante nella parte della mantissa e dell'esponente. Incrementare è facile: basta aggiungerne uno alla mantissa. Se ottieni un overflow devi gestirlo incrementando il tuo esponente. Il decremento funziona allo stesso modo.

MODIFICA : come sottolineato nei commenti è sufficiente incrementare il float nella sua rappresentazione binaria. L'overflow della mantissa incrementerà l'esponente, ed è esattamente quello che vogliamo.

In poche parole è la stessa cosa che fa il prossimo.

Questo non sarà completamente portatile però. Dovresti avere a che fare con endianess e il fatto che non tutte le macchine hanno float IEEE (ok - l'ultimo motivo è più accademico).

Anche gestire NAN e infiniti può essere un po 'complicato. Non puoi semplicemente incrementarli poiché per definizione non sono numeri.

Altri suggerimenti

u64 &x = *(u64*)(&f);
x++;

Sì, sul serio.

Modifica: Come qualcuno ha sottolineato, questo non tratta correttamente i numeri -ve, Inf, Nan o overflow. Una versione più sicura di quanto sopra è

u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 )    //if exponent is all 1's then f is a nan or inf.
{
    x += f>0 ? 1 : -1;
}

In termini assoluti, la quantità più piccola che è possibile aggiungere a un valore in virgola mobile per creare un nuovo valore distinto dipenderà dall'entità corrente del valore; sarà il machine epsilon del tipo moltiplicato per l'attuale esponente.

Consulta le Specifiche IEEE per la rappresentazione in virgola mobile. Il modo più semplice sarebbe reinterpretare il valore come un tipo intero, aggiungere 1, quindi verificare (se ti interessa) che non hai capovolto il segno o generato un NaN esaminando i bit di segno e esponente.

In alternativa, è possibile utilizzare frexp per ottenere l'attuale mantissa ed esponente, e quindi calcolare un valore da aggiungere.

Avevo bisogno di fare esattamente la stessa cosa e ho trovato questo codice:

double DoubleIncrement(double value)
{
  int exponent;
  double mantissa = frexp(value, &exponent);
  if(mantissa == 0)
    return DBL_MIN;

  mantissa += DBL_EPSILON/2.0f;
  value = ldexp(mantissa, exponent);
  return value;
}

Per quello che vale, il valore per cui l'incremento standard ++ cessa di funzionare è 9.007.199.254.740.992.

Potrebbe non essere esattamente quello che vuoi, ma potresti ancora trovare numeric_limits in uso. In particolare i membri min () e epsilon ().

Non credo che qualcosa come mydouble + numeric_limits :: epsilon () farà quello che vuoi, a meno che mydouble non sia già vicino a epsilon. Se lo è, allora sei fortunato.

Ho trovato questo codice qualche tempo fa, forse ti aiuterà a determinare il minimo che puoi spingerlo verso l'alto, quindi incrementarlo di quel valore. Purtroppo non ricordo il riferimento per questo codice:

#include <stdio.h>

int main()
{
    /* two numbers to work with */
    double number1, number2;    // result of calculation
    double result;
    int counter;        // loop counter and accuracy check

    number1 = 1.0;
    number2 = 1.0;
    counter = 0;

    while (number1 + number2 != number1) {
        ++counter;
        number2 = number2 / 10;
    }
    printf("%2d digits accuracy in calculations\n", counter);

    number2 = 1.0;
    counter = 0;

    while (1) {
        result = number1 + number2;
        if (result == number1)
            break;
        ++counter;
        number2 = number2 / 10.0;
    }

    printf("%2d digits accuracy in storage\n", counter );

    return (0);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top