【问题标题】:How to input and output real numbers in assembly language如何用汇编语言输入和输出实数
【发布时间】:2017-11-28 16:23:54
【问题描述】:

我们使用 FPU 解决汇编语言中的实数问题。通常我们使用C语言或者准备好的函数来编写输入输出代码。例如:

    ; Receiving input and output descriptors for the console
    invoke  GetStdHandle,   STD_INPUT_HANDLE
    mov     hConsoleInput,  eax

    invoke  GetStdHandle,   STD_OUTPUT_HANDLE
    mov     hConsoleOutput, eax

    invoke  ClearScreen
    ;input X
    invoke  WriteConsole, hConsoleOutput, ADDR aszPromptX,\
            LENGTHOF aszPromptX - 1, ADDR BufLen, NULL
    invoke  ReadConsole, hConsoleInput, ADDR Buffer,\
            LENGTHOF Buffer, ADDR BufLen, NULL
    finit
    invoke  StrToFloat, ADDR Buffer, ADDR X

如何在不使用现成函数的情况下,用汇编语言进行实数的输入输出?

【问题讨论】:

  • 这是一道作业题吗? We have an etiquette about those.
  • 它“太宽泛”了。你有什么特别的问题?你可以为整数做吗?请注意,在解决一些解析问题后,您可以按 10 的适当幂进行缩放以获得浮点数。
  • 你真正想做什么?打印正确舍入的浮点数的完整解决方案并不容易,即使在高级语言中也是如此。如果您只是想针对特定情况打印受限域中的数字,可能有更简单的解决方案,例如以固定精度打印几位数字。
  • 更新了我的答案,我忘记了 floatdouble 的整数部分可能不适合 64 位整数,因此您可能需要多个步骤来处理最大的 FP 值,即使不考虑小数部分。 (由于四舍五入,从最大数开始计算并不安全。)

标签: assembly io floating-point fpu real-number


【解决方案1】:

这与如何实现这些功能/它们如何在后台工作确实是同一个问题。我只想谈谈这个答案中的输入;我不确定哪些算法对 float->string 有好处。

操作系统提供的功能让您可以读取/写入(打印)字符,一次一个或一个块。问题中有趣的 / FP-specific 部分只是 float->string 和 string->float 部分。其他一切都与读取/打印整数相同(模调用约定差异:浮点数通常在 FP 寄存器中返回)。


如果您希望结果始终正确四舍五入到最接近的可表示 FP 值,则正确实现 strtod (string to double) 和单精度等效项是非常重要的,特别是如果您希望它也高效并适用于输入直到double 可以容纳的最大有限值的极限。

一旦您了解了算法的详细信息(就查看单个数字和执行 FP 乘法/除法/加法,或对 FP 位模式的整数运算而言),您可以在 asm 中为您的任何平台实现它像。出于某种原因,您在示例中使用了 x87 finit 指令。

请参阅http://www.exploringbinary.com/how-glibc-strtod-works/ 了解 glibc 实现的详细信息,http://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/ 了解另一种广泛使用的实现。

概述第一篇文章,glibc 的strtod 使用扩展精度整数 算术。它解析输入的十进制字符串以确定整数部分和小数部分。例如456.833e2(科学记数法)的整数部分为45683,小数部分为0.3

它将两个部分分别转换为浮点数。整数部分很简单,因为已经有硬件支持将整数转换为浮点数。例如x87 fild 或 SSE2 cvtsi2sd,或其他架构上的其他任何东西。但是如果整数部分大于最大64位整数,就没有那么简单了,需要将BigInteger转成float/double,硬件不支持。

请注意,即使是 IEEE binary32 的 FLT_MAX(单精度)float 也是 (2 − 2^−23) × 2^127,它只是 低于 2^128,因此您可以使用 128 位整数字符串->float,如果是这样,那么正确的float 结果是+InfinityFLT_MAX bit pattern is 0x7f7fffff:尾数全一 = 1.999... 最大指数。十进制是~3.4 × 10^38

但是,如果您不关心效率,我认为您可以将每个数字转换为 float(或索引已转换的 float 值的数组),然后执行通常的 total = total*10 + digit,或这个案例total = total*10.0 + digit_values[digit]。 FP mul / add 精确到两个相邻可表示值相距大于 1.0 的整数(即当 nextafter(total, +Infinity)total+2.0 时),即当 1 ulp 大于 1.0 时。

实际上,要获得正确的四舍五入,您需要先添加小值,否则它们各自单独向下四舍五入,当它们加在一起时,它们可能会将一个大值增加到下一个可表示的值。 p>

因此,如果您小心操作,您可能可以使用 FPU,例如以 8 位数字块工作并缩放 10^8 或其他东西,并从最小的开始添加。您可以将每个 8 位字符串转换为整数并使用硬件int->float


小数部分更复杂,尤其是如果您想避免重复除以 10 来获得位置值,您应该避免这种情况,因为它很慢并且因为 1/10 在二进制浮点,因此如果您以“明显”的方式进行操作,所有位值都会出现舍入错误。

但如果整数部分非常大,double 的所有 53 个尾数位可能已经由整数部分确定。所以 glibc 会进行检查,并且只进行大整数除法以从小数部分获取所需的位数(如果有的话)。

无论如何,我强烈建议您阅读这两篇文章。


顺便说一句,如果您不熟悉 IEEE754 binary64(又名 double)用来表示数字的位模式,请参阅 https://en.wikipedia.org/wiki/Double-precision_floating-point_format。您需要编写一个简单的实现,但它确实有助于理解 float。而对于 x86 SSE,您需要知道符号位在哪里来实现绝对值 (ANDPS) 或否定 (XORPS)。 Fastest way to compute absolute value using SSEabsneg 没有特殊说明,您只需使用布尔运算来操作符号位。 (比从零中减去要高效得多。)


如果您不关心是否准确到最后一个 ULP(最后一位的单位 = 尾数的最低位),那么您可以执行一个更简单的算法,即乘以 10 并添加类似字符串 -> 整数,最后按 10 的幂进行缩放。

但是一个健壮的库函数不能做到这一点,因为创建一个比最终结果大很多倍的临时值意味着对于@987654362范围内的某些输入,它将溢出(到+/- Infinity) @ 可以代表。或者如果您创建较小的临时值,可能会下溢到+/- 0.0

分别处理整数和小数部分可以避免溢出问题。

请参阅this C implementation on codereview.SE 以获取一个可能会溢出的非常简单的乘法/加法方法的示例。我只是快速浏览了一下,但我没有看到它拆分整数/小数部分。它只处理科学记数法E99 或末尾的任何内容,重复乘以或除以 10。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 2011-10-10
    • 2014-03-28
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    相关资源
    最近更新 更多