数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?

目录

数值溢出保护

数值溢出应该是多少?

检测溢出

溢出

有符号和无符号数的溢出

自动类型转换和溢出

Swift如何让整数计算溢出不抛出异常?

类型最大值和最小值

最大最小值


数值溢出保护

C/ObjC/C++在数值溢出时并未做过多保护,责任推给了程序员。

  • C/ObjC/C++选择相信程序员能控制溢出,不过有的编译器会提供溢出预防策略。
    char c = 128; // 假设char是有符号1字节
    - GCC/G++不会提示任何问题,gcc11.4即使用-Woverflow也不会有任何提示。
    - Clang会提示警告:implicit conversion from 'int' to 'char' changes value from 128 to -128
  • Go语言和C语言类似,选择相信程序员能控制溢出,发生溢出不会有任何错误。
  • Swift是设计安全的语言,编译期间发现数值存储不匹配,会直接报错,运行期发现溢出也会报错
    let a: UInt8 = -1
    同时,Swift也提供了溢出运算符(如&+&-&*等)来显式处理可能溢出的情况。
  • C#默认也不会对运行期溢出做处理,但可利用编译器选项/checked或者checked操作符/语句做全局、局部溢出检查,一旦发现溢出将抛出异常。
    • 代码或者编译选择指定checked,一定会有溢出检查;如果代码中指定unchecked, 会忽略编译选项/checked.
    • 注意,如果用常量赋值发生溢出,如short a = 32767,编译器会直接报错:常量值“32768”无法转换为“short”
  • Java和C#一样常量赋值逃不过编译器检查,默认也不会对运行期溢出做处理,运行期可利用特定API做溢出检查,比如加法检查Math.addExact方法。
    int a = Integer.MAX_VALUE;
    try {
        int result = Math.addExact(a, 1);
    } catch (ArithmeticException e) {
        System.out.println("Overflow exceptoin:" + e.getMessage());
    }
  • Python 3用long替代int, long几乎是无穷大(除非计算机内存无法存储long字符串),数值溢出很少出现在Python.
  • JS作为脚本语言,溢出是个很少考虑的话题。数值Number类型最大安全值MAX_SAFE_INTEGER超过万亿,如果不满足还可用更大的BigInt.
  • VB.NET之前编译阶段不会提示溢出,运行期溢出会报错。VB.NET编译期即可报错。
    - 例如:Dim bigNumber As Integer = Int32.MaxValue + 1
    报错:常量表达式无法在类型“Integer”中表示。
  • Rust作为一门安全放在首位的语言,编译期发现溢出会报错。
    let a: u32 = u32::MAX + 1; // 报错:arithmetic operation will overflow
    • 同时Rust提供一系列API反馈是否会溢出、溢出后是回绕还是设定最大/最小值。
      以加法为例,checked_add发生溢出返回None,overflowing_add返回溢出与否标志和计算值,saturating_add根据是上溢还是下溢设置为能表达的最大或最小值,wrapping_add会在发生溢出时回绕。
  • 仓颉 遇到溢出的字面量,一样会编译出错。
    var i: UInt8 = 300
    报错:the number '300' exceeds the value range of type 'UInt8'
  • PHP超出整数范围的数值会自动转换成浮点数以保证数值正确。

数值溢出应该是多少?

大家知道,因为寄存器和内存单元容量有限,数值不可能无限大。语言标准规定数值溢出可能是未定义的行为。一种常见的说法是,有符号数才有溢出的概念,无符号数没有溢出的概念,只有回绕。

  • C语言无符号整型溢出会回绕,有符号整型溢出行为不确定。
    一般而言,编译器的实现会遵循汇编指令溢出的状态或者按照特定常规计算算法,例如计算一个十进制整型字符串的数值,会按位累加乘10, 最终的数值即为溢出的值。
  • PHP超出整数范围的数值会自动转换成浮点数以保证数值正确。

检测溢出

  • 以int加法为例,不能单纯用a + b > INT_MAX来检测,因为a + b很可能已经是负数。常见的检查方法是检查EFLAGS b'11 OF位是否为1.

溢出

任何数据类型都有最大和最小值,会溢出是由物理特性决定。编译型静态类型语言一般明确定义类型大小,这种定义出来的类型都存在溢出的可能。有的动态语言为减少程序员溢出的困扰,尽可能为数据扩展成更大的数值,Python就是这样的好人。

  • Python中计算2的100次幂2**100,结果正确。Python 2有int和long区分,Python 3只有long类型, long是不溢出的。原理在于long内部保存了一个整型数组,保存每位。
  • C/C++/Java/C#/Go/Rust原生均不支持无限长整型,除非用数组或字符串模拟大整数运算。
  • PHP提供bcadd之类或GMP函数完成大整数计算。
  • JS的BigInt可以表示任意大的整数。
  • PHP超出整数范围的数值会自动转换成浮点数以保证数值正确。

有符号和无符号数的溢出

  • C/C++有符号数溢出结果无定义,无符号数溢出是确定的,结果是2的n次幂取模。
  • 大部分编程语言数值存储用补码,例如short最小值是-32768,最大值是32767,如short变量n是负数,-n很可能溢出。

自动类型转换和溢出

需要注意,编程语言一般有隐式或自动类型转换,尽管表达式计算结果看起来超过类型大小,但因为有隐式转换,溢出并不会发生。

  • short b = 20000; int a = b * b;
    当short是2字节,int是4字节,b * b会变成int类型b相乘,结果在int类型范围,不会溢出。

Swift如何让整数计算溢出不抛出异常?

Swift提供了 &+、&-和&*三个溢出操作符,在发生溢出时,不抛出异常,而是正常运行下去。

类型最大值和最小值

每种类型都对应于原始的存储位数,也就间接产生了最大和最小值。水满自溢同理。对于一些复合类型,比如封装的字符串,利用堆空间并可以自动增大,理论上长度可以无限,受限于计算机硬件内存和长度变量类型。

最大最小值

以整形为例:

  • C/C++提供形如INT_MAX或INT_MIN宏。
    • C++98 STL numeric_limits<int>::max() or min()提供同样功能,C++11额外增加了lowest().
      • 浮点数比较特殊,lowest代表最小负数,min代表最小浮点正数。
  • Java: 形如Integer.MAX_VALUE和Integer.MIN_VALUE.
  • C#: 形如int.MaxValue和int.MinValue.
  • Python给出整形最大值或最小值意义不大,当超出范围,自动扩展,即整数可以想象中是无限增大不溢出(受限于系统内存)。
    • 64位整数最大和最小值:sys.maxsize和-sys.maxsize - 1
      • 9223372036854775807 -922337203685477580
  • JS: Number有最大安全整数和最小安全整数定义,更大的整数用BigInt.
    • Number.MAX_SAFE_INTEGER(253 - 1或9007199254740991)和Number.MIN_SAFE_INTEGER(-253 + 1或-9007199254740991)


若文章对您有帮助,欢迎关注 程序员小迷 。助您在编程路上越走越好!

微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是 程序员小迷 (致力于C、C++、C#、Android、iOS、Java、Kotlin、Objective-C、Swift、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值