¿Existe una forma estándar de obtener el enésimo valor de punto flotante `nextafter` en C ++


8

C ++ tiene std::nextafter(), que devuelve el siguiente valor representable después de un valor de punto flotante dado f . En mi caso, me gustaría permitir n bits de slop en los bits de mantisa inferiores, por lo que 3 bits de slop requerirían obtener el octavo valor siguiente después de cierto valor dado f . Podría llamar nextafter()ocho veces, pero ¿hay una mejor manera de manejar esto?

Para la mayoría de los valores, podría pasar con la conversión del valor FP a uint_64, agregando la tolerancia ( 1<<3para 3 bits de pendiente) y luego volviendo a double, gracias al diseño de IEEE 754. Sin embargo, eso se basa en el punto flotante IEEE 754 ( una buena suposición, pero tampoco sólida como una roca).

(Para el fondo, quiero usar esto para izar puntos de intersección de la superficie del rayo, que ocasionalmente se encuentran dentro de la superficie debido a la imprecisión de FP. Aquellos familiarizados con el punto flotante robusto entenderán por qué epsilones una solución terrible).


No parece que necesite exactamente el octavo valor siguiente. ¿Multiplicar f (suponiendo que sea positivo) con 1.00 ... 001 sería lo suficientemente bueno?
Marc Glisse

También puede usar el valor de std::numeric_limits<T>::is_iec559para verificar si se usa IEEE 754 y especializar la función en consecuencia.
IlCapitano

Respuestas:


2

Un enfoque ingenuo podría ser multiplicar por 8 la distancia entre un valor y el siguiente flotante representable, en lugar de llamar 8 veces std::nextafter

double advance_float(double x, int d)
{
    double step = std::copysign((std::nextafter(x, x + d) - x) * d, d);
    return x + step;
}

Aquí hay algunas pruebas, pero depende de usted determinar si esto es adecuado para su caso de uso.

Editar

Como señaló Steve Hollash , xpuede ser tan grande que x + d == d. Daniel Jour sugirió aprovechar frexp(y ldexp), pero en el siguiente intento, usaré un enfoque diferente para determinar la dirección.

double advance_float(double x, int d)
{
    const double to = std::copysign(std::numeric_limits<double>::infinity(), d);
    const double next = std::nextafter(x, to);
    return x + std::copysign(d * (next - x), d);
}

Tenga en cuenta que se supone que std::numeric_limits<double>::has_infinity == true, de lo contrario ::lowest(), y ::max()debe ser utilizado.

Esos son algunos resultados

         xd anterior x siguiente
-------------------------------------------------- ----------------------------------------
           1 1 0x1.fffffffffffffp-1 0x1p + 0 0x1.0000000000001p + 0
           1 8 0x1.ffffffffffff8p-1 0x1p + 0 0x1.0000000000008p + 0
     3.14159 8 0x1.921fb54442d1p + 1 0x1.921fb54442d18p + 1 0x1.921fb54442d2p + 1
      100.01 8 0x1.900a3d70a3d69p + 6 0x1.900a3d70a3d71p + 6 0x1.900a3d70a3d79p + 6
     -100.01 8 -0x1.900a3d70a3d79p + 6 -0x1.900a3d70a3d71p + 6 -0x1.900a3d70a3d69p + 6
       1e + 67 8 0x1.7bd29d1c87a11p + 222 0x1.7bd29d1c87a19p + 222 0x1.7bd29d1c87a21p + 222
       1e-59 8 0x1.011c2eaabe7dp-196 0x1.011c2eaabe7d8p-196 0x1.011c2eaabe7ep-196
           0 8 -0x0.0000000000008p-1022 0x0p + 0 0x0.0000000000008p-1022
4.94066e-324 8 -0x0.0000000000007p-1022 0x0.0000000000001p-1022 0x0.0000000000009p-1022

Enfoque interesante x+dSin embargo, como está escrito, no es lo que estás buscando. Si x es grande, entonces (x + d) == x. Pero me gusta la idea: calcule el valor igual al bit de orden más bajo con un exponente coincidente, escalado por la "pendiente", y luego agregado al valor original.
Steve Hollasch

@SteveHollasch Es cierto, ese es el problema con nextaftercómo pasar la "dirección".
Bob__

Agregar frexpantes de esto y ldexpdespués debería funcionar, ¿no?
Daniel Jour

@DanielJour ¿Quieres decir esto ? Parece que "funciona", pero seguramente me faltan muchos casos de esquina.
Bob__

Maravilloso. No es solo una solución, sino un montón de nuevas herramientas que no conocía. Con frexpen particular, puedo mover mundos. ¡Gracias!
Steve Hollasch

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.