十六进制浮点数字面量

11

在C++中是否可能使用十六进制浮点值初始化浮点变量?

类似于这样:

double d = 0x011.1; // wrong!

1
不是那么简单,但你可以将 int 转换为 float,但需要了解给定平台上 float 的字节表示方式。 - Creris
2
你可以在C99和C++17中使用,但不支持你的语法。http://en.cppreference.com/w/cpp/language/floating_literal - phuclv
2个回答

21

技术规范P0245 C++十六进制浮点字面量于2016年2月在佛罗里达州杰克逊维尔的ISO C++标准委员会上被投票纳入C++17。

语言C99也具有此功能,而且C++功能是兼容的。

然而,正如Lưu Vĩnh Phúc的评论所指出的那样,语法0x011.1不是标准的一部分。十六进制浮点字面量需要二进制指数。原因之一是避免在0x011.1F中的结尾F引起歧义。它是小数部分的十六进制数字F还是表示float的浮动后缀? 因此,在p后附加一个正或负的十进制数,例如:0x011.1p0

请查看更易读的页面cppreference.com上的浮点文字面值页面

0x | 0X 十六进制数
0x | 0X 十六进制数 .
0x | 0X 十六进制数(可选) . 十六进制数

十六进制数字序列表示一个没有基数分隔符的整数。对于十六进制浮点文字,指数永远不是可选的:0x1ffp100X0p-10x1.p00xf.p-10x0.123p-10xa.bp10l

十六进制浮点文字的指数语法形式为
p | P 指数符号(可选) 数字序列

如果存在指数符号,则为 + 或 -

如果存在后缀,则为 f, F, l, 或 L 中的一种。 后缀决定了浮点文字的类型:

  • (无后缀) 定义 double
  • f F 定义 float
  • l L 定义 long double
请参阅 GitHub 上的 C++17 当前工作草案,第§ 2.13.4 浮点字面量章节:https://github.com/cplusplus/draft/raw/master/papers/n4604.pdf

浮点字面量:
十进制浮点字面量
十六进制浮点字面量
十进制浮点字面量:
小数常量 指数部分(可选) 浮点后缀(可选)
数字序列 指数部分 浮点后缀(可选)
十六进制浮点字面量:
十六进制前缀 十六进制小数常量 二进制指数部分 浮点后缀(可选)
十六进制前缀 十六进制数字序列 二进制指数部分 浮点后缀(可选)
小数常量:
数字序列(可选) . 数字序列
数字序列 .
十六进制小数常量:
十六进制数字序列(可选) . 十六进制数字序列
十六进制数字序列 .
指数部分:
e 符号(可选) 数字序列
E 符号(可选) 数字序列
二进制指数部分:
p 符号(可选) 数字序列
P 符号(可选) 数字序列
符号: 为以下之一
+ -
数字序列:
数字
数字序列 ’ 数字
浮点后缀: 为以下之一
f l F L

1 浮点字面量由一个可选前缀、一个整数部分、一个基数点、一个小数部分、一个 eEpP、一个可选的带符号整数指数和一个可选的类型后缀组成。如果没有前缀,则整数部分和小数部分均由十进制(十进制)数字序列组成,否则由十六进制(十六进制)数字序列组成。在前一种情况下,字面量是一个“十进制浮点字面量”,在后一种情况下,字面量是一个“十六进制浮点字面量”。在数字序列或十六进制数字序列中可选的分隔单引号在确定其值时被忽略。[例子:字面量 1.602’176’565e-191.602176565e-19 具有相同的值。—结束例子] 整数部分和小数部分(不是两者都)可以省略。十进制浮点字面量可以省略基数点或字母 e 或 E 和指数(不是两者都)。十六进制浮点字面量可以省略基数点(但不是

正如 unwind 所建议的,您可以使用 strtof()。以下代码片段解码十六进制浮点文字(不包括C++17):

#include <iostream>
#include <cstdlib>
#include <cstdio>

int main(int argc, char *argv[])
{
  if (argc != 2)
  {
    std::cout <<"Usage: "<< argv[0] <<" 0xA.Bp-1  => Decode hexfloat" "\n";
    return 1;
  }

  long double l;
  double      d;
  float       f;

  std::cout <<"Decode floating point hexadecimal = "<< argv[1];
  //std::istringstream(argv[1]) >> std::hexfloat >> d;
  l = std::strtold(argv[1],NULL); if(errno == ERANGE) std::cout << "\n" "std::strtold() range error";
  d = std::strtod (argv[1],NULL); if(errno == ERANGE) std::cout << "\n" "std::strtod() range error";
  f = std::strtof (argv[1],NULL); if(errno == ERANGE) std::cout << "\n" "std::strtod() range error";

  std::cout <<"\n"  "long double = "<< std::defaultfloat << l <<'\t'<< std::hexfloat << l
            <<"\n"  "double      = "<< std::defaultfloat << d <<'\t'<< std::hexfloat << d
            <<"\n"  "float       = "<< std::defaultfloat << f <<'\t'<< std::hexfloat << f <<'\n';
}

1

不,C++标准不支持使用这种字面值,它不是标准的一部分。

一个非可移植的解决方案是使用添加了此功能的编译器扩展(GCC支持此操作)。

一个可移植的解决方案是在运行时从字符串字面量中解析它们,例如使用 strtof()strtod() 用于double

正如评论中指出的那样,您还可以选择将常量存储在C文件中。这样做需要您可以访问C99编译器,因为十六进制浮点字面量是C99级别的特性。由于具有新C++编译器但没有C99编译器的环境(例如Visual Studio)相当普遍,这可能不是可行的解决方案。

更新: C++17支持十六进制浮点字面量。


2
如果要使用运行时解决方案,最好直接在C中定义外部浮点对象,使用所需的十六进制浮点数,并将它们链接起来。这至少几乎没有运行时成本,不像strtof(它产生float,但OP可能需要double)。这将生成除使用支持十六进制浮点数的C++编译器或使用自定义预处理器将十六进制浮点数转换为十进制数(并使用将十进制数转换为浮点数的C++编译器)之外的最佳代码。 - Eric Postpischil
2
当然,C++11有用户自定义字面量。它们支持任何您想要支持的内容。十六进制浮点数很容易实现。 - MSalters
@MSalters 哦。这个页面持不同意见,但请随意详细说明或发布一个可行的实现链接,我会进行编辑。 - unwind
@unwind:该页面并不反对,它只是指出您无法使用C++11用户定义的字面量实现C内置语法。此外,请阅读有关“原始”字面量的部分。 - MSalters
使用十六进制浮点数实际上是不可移植的吗?也就是说,现代编译器是否都支持它们? - tmyklebu
2
C++17将支持十六进制浮点数。 - Kerrek SB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接