Wie kann man einen Schwimmer mit seinem kleinsten Inkrement (oder in der Nähe) verändern?

StackOverflow https://stackoverflow.com/questions/155378

  •  03-07-2019
  •  | 
  •  

Frage

Ich habe ein double Wert f und möchte einen Weg, um es etwas größer (oder kleiner) zu schieben, um einen neuen Wert zu erhalten, der dem ursprünglichen, aber immer noch streng größer als (oder weniger als) dem Original ist.

Es muss nicht bis zum letzten Stück dicht sein - es ist wichtiger, dass jede Änderung, die ich vornehme, garantiert einen anderen Wert und nicht zurück zum Original zurückgeben.

War es hilfreich?

Lösung

Überprüfen Sie Ihre math.h.h -Datei. Wenn Sie Glück haben, haben Sie das nextafter und nextafterf Funktionen definiert. Sie tun genau das, was Sie auf tragbare und plattformunabhängige Weise wollen, und sind Teil des C99 -Standards.

Eine andere Möglichkeit, dies zu tun (könnte eine Fallback -Lösung sein), besteht darin, Ihren Schwimmer in die Mantissa und den Exponent -Teil zu zersetzen. Inkrementierung ist einfach: Fügen Sie einfach einen zur Mantissa hinzu. Wenn Sie einen Überlauf erhalten, müssen Sie dies bearbeiten, indem Sie Ihren Exponenten erhöhen. Die Dekrementierung funktioniert auf die gleiche Weise.

BEARBEITEN: Wie in den Kommentaren ausgeführt, reicht es aus, den Schwimmer in seiner binären Darstellung nur zu erhöhen. Der Mantissa-Überfluss wird den Exponenten erhöht, und genau das wollen wir.

Das ist auf diese Weise das gleiche, was als nächstes nach dem Vorbeigehen geht.

Dies wird jedoch nicht vollständig tragbar sein. Sie müssten sich mit Endialtess und der Tatsache befassen, dass nicht alle Maschinen IEEE -Schwimmer haben (OK - der letzte Grund ist akademischer).

Außerdem kann es etwas schwierig sein, Nan und Infinite zu bearbeiten. Sie können sie nicht einfach inkrementieren, da sie per Definition nicht Zahlen sind.

Andere Tipps

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

Ja, im Ernst.

Bearbeiten: Wie jemand betonte, handelt es sich nicht um -ve -Zahlen, INF, Nan oder Überlauf. Eine sicherere Version des oben genannten ist

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 absoluter Begriffen hängt die kleinste Menge, die Sie zu einem schwimmenden Punktwert hinzufügen können, um einen neuen unterschiedlichen Wert zu erzielen, von der aktuellen Größe des Wertes ab. Es wird der Typ sein Maschine Epsilon multipliziert mit dem aktuellen Exponenten.

Probier das aus IEEE Spec Für schwimmende Punkte Repräsentation. Der einfachste Weg wäre, den Wert als Ganzzahltyp neu zu interpretieren, 1 hinzuzufügen, dann zu überprüfen (wenn Sie sich darum kümmern), dass Sie das Zeichen nicht umgedreht oder eine Nan generiert haben, indem Sie das Zeichen und die Exponent -Bits untersuchen.

Alternativ könnten Sie verwenden Frexp Um den aktuellen Mantissa und den Exponenten zu erhalten und somit einen zu addierten Wert zu berechnen.

Ich musste genau dasselbe tun und habe diesen Code entwickelt:

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;
}

Für das, was es wert ist, beträgt der Wert, für den Standard ++ Inkrementierung funktioniert, 9.007.199.254.740.992.

Dies ist vielleicht nicht genau das, was Sie wollen, aber Sie könnten immer noch finden numeric_limits in Nutzung. Insbesondere die Mitglieder min () und Epsilon ().

Ich glaube nicht, dass so etwas wie MyDouble + numeric_limits :: epsilon () das tun wird, was Sie wollen, es sei denn, MyDouble ist schon nahe an Epsilon. Wenn ja, dann haben Sie Glück.

Ich habe diesen Code vor einiger Zeit gefunden, vielleicht hilft Ihnen, die kleinsten zu bestimmen, die Sie bis dahin nach oben drücken können. Leider kann ich mich nicht an die Referenz für diesen Code erinnern:

#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);
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top