获取整数的负号并将其存储为 char 的最佳方法是什么?
What is the best way to get integer's negative sign and store it as char?
如何获取整数的符号并将其存储在字符中?一种方式是:
int n = -5
char c;
if(n<0)
c = '-';
else
c = '+';
或者:
char c = n < 0 ? '-' : '+';
但是有没有不用条件的方法呢?
这会在 x86-64 上使用 gcc/clang 创建无分支代码:
void storeneg(int X, char *C)
{
*C='+';
*C += (X<0)*('-'-'+');
}
有最高效便携的方式,却没有获得任何美妆大奖
我们可以假设有符号整数的 MSB 总是设置为负数。这是一个 100% 可移植的假设,即使考虑到奇异的符号格式(一个人的补码,有符号的幅度)也是如此。因此,最快的方法是简单地从整数中屏蔽掉 MSB。
任何整数的 MSB 位于位置 CHAR_BIT * sizeof(n) - 1;
。在典型的 32 位主流系统上,例如 8 * 4 - 1
= 31.
所以我们可以这样写一个函数:
_Bool is_signed (int n)
{
const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
return (_Bool) ((unsigned int)n >> sign_bit_n);
}
在 x86-64 gcc 9.1 (-O3) 上,这会产生非常高效的代码:
is_signed:
mov eax, edi
shr eax, 31
ret
此方法的优点还在于,与 x < 0
等代码不同,它不会在移植时冒被翻译成 "branch if negative" 指令的风险。
完整示例:
#include <limits.h>
#include <stdio.h>
_Bool is_signed (int n)
{
const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
return (_Bool) ((unsigned int)n >> sign_bit_n);
}
int main (void)
{
int n = -1;
const char SIGNS[] = {' ', '-'};
char sign = SIGNS[is_signed(n)];
putchar(sign);
}
反汇编(x86-64 gcc 9.1 (-O3)):
is_signed:
mov eax, edi
shr eax, 31
ret
main:
sub rsp, 8
mov rsi, QWORD PTR stdout[rip]
mov edi, 45
call _IO_putc
xor eax, eax
add rsp, 8
ret
char c = 43 + signbit(n) * 2 ;
char 43
是 '+'
char 45
是 '-'
- signbit(NEGATIVE INTEGER) 是
true
,转换为 1
int signbit(int)
包含在 cmath
in C++ and math.h
in C
中
如何获取整数的符号并将其存储在字符中?一种方式是:
int n = -5
char c;
if(n<0)
c = '-';
else
c = '+';
或者:
char c = n < 0 ? '-' : '+';
但是有没有不用条件的方法呢?
这会在 x86-64 上使用 gcc/clang 创建无分支代码:
void storeneg(int X, char *C)
{
*C='+';
*C += (X<0)*('-'-'+');
}
有最高效便携的方式,却没有获得任何美妆大奖
我们可以假设有符号整数的 MSB 总是设置为负数。这是一个 100% 可移植的假设,即使考虑到奇异的符号格式(一个人的补码,有符号的幅度)也是如此。因此,最快的方法是简单地从整数中屏蔽掉 MSB。
任何整数的 MSB 位于位置 CHAR_BIT * sizeof(n) - 1;
。在典型的 32 位主流系统上,例如 8 * 4 - 1
= 31.
所以我们可以这样写一个函数:
_Bool is_signed (int n)
{
const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
return (_Bool) ((unsigned int)n >> sign_bit_n);
}
在 x86-64 gcc 9.1 (-O3) 上,这会产生非常高效的代码:
is_signed:
mov eax, edi
shr eax, 31
ret
此方法的优点还在于,与 x < 0
等代码不同,它不会在移植时冒被翻译成 "branch if negative" 指令的风险。
完整示例:
#include <limits.h>
#include <stdio.h>
_Bool is_signed (int n)
{
const unsigned int sign_bit_n = CHAR_BIT * sizeof(n) - 1;
return (_Bool) ((unsigned int)n >> sign_bit_n);
}
int main (void)
{
int n = -1;
const char SIGNS[] = {' ', '-'};
char sign = SIGNS[is_signed(n)];
putchar(sign);
}
反汇编(x86-64 gcc 9.1 (-O3)):
is_signed:
mov eax, edi
shr eax, 31
ret
main:
sub rsp, 8
mov rsi, QWORD PTR stdout[rip]
mov edi, 45
call _IO_putc
xor eax, eax
add rsp, 8
ret
char c = 43 + signbit(n) * 2 ;
char 43
是'+'
char 45
是'-'
- signbit(NEGATIVE INTEGER) 是
true
,转换为1
int signbit(int)
包含在 cmath
in C++ and math.h
in C