这里有一个算法可以解决你的问题,但依赖于一些位旋转。
先计算并存储
x_attempt = 1.0 / (double) y
该值将非常接近您想要的 x 值,但由于浮点数学中的近似值,它可能会有些偏差。
现在检查x_attempt * (double) y 的值。如果该值不严格小于1.0,则将x_attempt 的值“微调”到下一个可以存储在双精度变量中的较小值。再次检查x_attempt * (double) y 并继续向下移动,直到该值严格小于1.0。这使用了一个循环,但它执行的次数很少,假设您的平台上的浮点运算完全没有问题。
如果该值严格小于1.0,则“微调”x_attempt 的值,直到该乘积为1.0 或更大。然后将x设置为x_attempt之前的值。
当那是我的首选语言时,我在 Borland Delphi 中实现了我自己的“轻推”例程。询问您在为您的语言和环境编写此类例程时是否需要帮助。
您的问题促使我用我当前的首选语言 Python 3.7 编写“微调”上下例程。它们在这里,没有我的单元测试例程。
import math
MIN_DOUBLE = 2 ** -1074 # 4.940656458412465442e-324
MIN_DOUBLE_DENORMAL = MIN_DOUBLE
MIN_DOUBLE_NORMAL = 2 ** -1022 # 2.225073858507201383e-308
MAX_DOUBLE = 1.7976931348623157082e308 # just under 2 ** 1024
EPSILON_DOUBLE_HALF = 2 ** -53 # changes most significand values
EPSILON_DOUBLE_FOURTH = 2 ** -54 # changes significand of -0.5
def nudged_up(x: float) -> float:
"""Return the next larger float value.
NOTES: 1. This routine expects that `float` values are implemented
as IEEE-754 binary64 and includes denormal values. No
check is done on these assumptions.
2. This raises an OverflowError for MAX_DOUBLE. All other
float values do not raise an error.
3. For +INF, -INF, or NAN, the same value is returned.
"""
if x == 0.0:
return MIN_DOUBLE_DENORMAL
significand, exponent = math.frexp(x)
if exponent < -1021: # denormal
return x + MIN_DOUBLE_DENORMAL
if significand == -0.5: # nudging will change order of magnitude
diff = EPSILON_DOUBLE_FOURTH
else: # usual case
diff = EPSILON_DOUBLE_HALF
return math.ldexp(significand + diff, exponent)
def nudged_down(x: float) -> float:
"""Return the next smaller float value.
NOTES: 1. This routine expects that `float` values are implemented
as IEEE-754 binary64 and includes denormal values. No
check is done on these assumptions.
2. This raises an OverflowError for -MAX_DOUBLE. All other
float values do not raise an error.
3. For +INF, -INF, or NAN, the same value is returned.
"""
return -nudged_up(-x)
这里是 Python 3.7 代码,可以回答您的问题。请注意,如果输入参数为零,则会引发错误——您可能需要更改它。
def lower_reciprocal(y: int) -> float:
"""Given a (32-bit) integer y, return a float x for which
x * float(y) is as large as possible but strictly less than 1.
NOTES: 1. If y is zero, a ZeroDivisionError exception is raised.
"""
if y < 0:
return -lower_reciprocal(-y)
float_y = float(y)
x = 1.0 / float_y
while x * float_y < 1.0:
x = nudged_up(x)
while x * float_y >= 1.0:
x = nudged_down(x)
return x