如何打印 IEEE754 数字(不使用 printf)?
就这个问题而言,我不有能力使用printf
设施(不幸的是,我无法告诉你为什么,但我们现在假设我知道我在做什么)。
对于 IEEE754 单精度数,您具有以下位:
SEEE EEEE EFFF FFFF FFFF FFFF FFFF FFFF
其中 S
是符号,E
是指数,F
是分数。
对于所有情况,打印符号都相对容易,捕获所有特殊情况也很容易,例如 NaN
(E == 0xff, F != 0
)、Inf< /code> (
E == 0xff, F == 0
) 和 0
(E == 0, F == 0
, 视为特殊只是因为在这种情况下不使用指数偏差)。
我有两个问题。
第一个是如何最好地将非规范化数字(其中 E == 0, F != 0
)转换为规范化数字(其中 1 <= E <= 0xfe
) ?我怀疑这对于简化下一个问题的答案是必要的(但我可能是错的,所以请随意教育我)。
第二个问题是如何打印出标准化后的数字。我希望能够以两种方式打印它们,指数(如 -3.74195E3
)和非指数(如 3741.95
)。不过,只要并排看这两个,就可以很容易地通过移动小数点将前者变成后者。所以我们只关注指数形式。
我对很久以前用来打印 PI 的算法有一个模糊的记忆,其中使用一个不断减少的公式并保留可能性的上限和下限,当两个限制都同意时输出一个数字,然后进行移位按因子 10 进行计算(因此当上限和下限分别为
3.2364
和3.1234
时,您可以输出3
并进行调整计算中)。但是自从我这样做以来已经很长时间了,所以我什至不知道这是否是一个合适的方法。看起来是这样,因为在移动小数部分时,每个位的值都是前一位的一半(
1/2
、1/4
、1/ 8
等等)。
除非绝对必要,否则我真的希望不必费力地浏览 printf
源代码,因此,如果有人可以提供帮助,我将永远感激不已。
For the purposes of this question, I do not have the ability to use printf
facilities (I can't tell you why, unfortunately, but let's just assume for now that I know what I'm doing).
For an IEEE754 single precision number, you have the following bits:
SEEE EEEE EFFF FFFF FFFF FFFF FFFF FFFF
where S
is the sign, E
is the exponent and F
is the fraction.
Printing the sign is relatively easy for all cases, as is catching all the special cases like NaN
(E == 0xff, F != 0
), Inf
(E == 0xff, F == 0
) and 0
(E == 0, F == 0
, considered special just because the exponent bias isn't used in that case).
I have two questions.
The first is how best to turn denormalised numbers (where E == 0, F != 0
) into normalised numbers (where 1 <= E <= 0xfe
)? I suspect this will be necessary to simplify the answer to the next question (but I could be wrong so feel free to educate me).
The second question is how to print out the normalised numbers. I want to be able to print them out in two ways, exponential like -3.74195E3
and non-exponential like 3741.95
. Although, just looking at those two side-by-side, it should be fairly easy to turn the former into the latter by just moving the decimal point around. So let's just concentrate on the exponential form.
I have a vague recollection of an algorithm I used long ago for printing out PI where you used one of the ever-reducing formulae and kept an upper and lower limit on the possibilities, outputting a digit when both limits agreed, and shifting the calculation by a factor of 10 (so when the upper and lower limits were
3.2364
and3.1234
, you could output the3
and adjust for that in the calculation).But it's been a long time since I did that so I don't even know if that's a suitable approach to take here. It seems so since the value of each bit is half that of the previous bit when moving through the fractional part (
1/2
,1/4
,1/8
and so on).
I would really prefer not to have to go trudging through printf
source code unless absolutely necessary so, if anyone can help out with this, I'll be eternally grateful.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您希望每次转换都获得准确的结果,则必须使用任意精度算术,如 printf() 实现中所做的那样。如果您想获得“接近”的结果,可能仅在最低有效数字上有所不同,那么一个非常简单的基于双精度的算法就足够了:对于整数部分,重复除以十并将余数附加到形成十进制字符串(相反);对于小数部分,重复乘以十并减去整数部分以形成十进制字符串。
我最近写了一篇关于此方法的文章: http:// /www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ 。它不打印科学计数法,但添加它应该很简单。该算法打印次正常数字(我打印的数字准确无误,但您必须进行更彻底的测试)。
If you want to get exact results for every conversion, you'll have to use arbitrary-precision arithmetic, as done in printf() implementations. If you want to get results that are "close," perhaps differing only in their least significant digit(s), then a very simple double-precision based algorithm will suffice: for the integer part, repeatedly divide by ten and append the remainders to form the decimal string (in reverse); for the fractional part, repeatedly multiply by ten and subtract off the integer parts to form the decimal string.
I recently wrote an article about this method: http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ . It does not print scientific notation, but that should be trivial to add. The algorithm prints subnormal numbers (the ones I printed came out accurately, but you'd have to do more thorough testing).
非规格化数无法转换为相同浮点类型的规格化数。等效归一化数的指数将太小而无法用指数表示。
要打印标准化数字,我能想到的一个愚蠢的方法是重复乘以 10(好吧,对于小数部分)。
Denormalized numbers cannot be turned into normalized numbers of the same floating point type. The equivalent normalized number's exponent will be too small to be represented by the exponent.
To print normalized numbers, one silly way I can think of is to repeatedly multiply by 10 (well, for the fractional part).
您需要做的第一件事是使用对数将指数转换为十进制(因为大概这就是您想要的输出)。您获取该结果的分数,并将尾数乘以该分数的 exp10,然后将其转换为十进制字符。从那里,您只需将小数点插入适当的位置,并按现在的十进制指数移动。
The first thing you need to do is convert the exponent to decimal (since presumably that's what you want the output in) using logarithms. You take the fraction of that result and multiply the mantissa by the exp10 of that fraction, and then convert that to decimal characters. From there you just need to insert the decimal point in the appropriate location, shifted by the now-decimal exponent.
G. Steele 有一篇论文更详细地描述了一种算法,该算法似乎基于与您概述的原理相同的原理。如果没记错的话,有时您会被迫使用无限制的精度算术。 (我认为这是如何准确打印浮点数,但 citeseer 目前已从这里开始,我无法确认,谷歌结果也被 20 年后的回顾性论文污染了) 。
There is a paper by G. Steele describing in more details an algorithm which seems based on the same principle as the one you outline. If memory serve, there are time when you are forced to use unbounded precision arithmetic. (I think it is How to print floating-point numbers accurately but citeseer is currently down from here, I can't confirm and google results are polluted by a retrospective paper by the same from 20 years later).