为什么可以打印小于 DBL_MIN 的双数?
why a double number less than DBL_MIN can be printed?
我把0.4543543234343654632452452525254e-323
赋值给一个double
变量a
打印出来,虽然小于DBL_MIN
,还是可以赋值打印出来的。
DBL_MAX: 1.79769e+308
FLT_MAX: 3.40282e+38
DBL_MIN: 2.22507e-308
FLT_MIN: 1.17549e-38
a: 4.94066e-324
为什么会这样?
实际上DBL_MIN
不是最小值而是可表示的最小值normalized value。
不同之处在于,规范化值的前导数字为 1,非规范化数字的前导数字为 0。请注意,denormal numbers 可能会遇到无法在硬件中管理浮动处理单元的硬件的严重性能问题。
但是您的值 0.454354e-323
对应于 4.545354e-324
小于用 double
表示的最小非正规数,实际上它四舍五入为 4.94066e-324
是可以存储在 double
.
中的最小实数
直接查看位更容易理解 IEEE 754-1985 行为。
following program 显示每个数字的符号、尾数和指数以及每个数字的位模式。
#include <iostream>
#include <limits>
#include <iomanip>
#include <bitset>
#include <unordered_map>
#include <string>
#include <tuple>
#include <sstream>
#include <cmath>
using Values = std::tuple<double,std::string>;
using MyMap = std::unordered_map<std::string,Values>;
std::string convert_to_string(double val)
{
auto ptr{reinterpret_cast<const unsigned long long*>(&val)};
auto ival{*ptr};
unsigned long long mask{1ULL << 63};
std::string bitstring;
for (size_t i{0}; i<64; ++i) {
auto bitval{(ival&mask)>0};
mask >>= 1;
bitval? bitstring.push_back('1') : bitstring.push_back('0');
}
return bitstring;
}
std::ostream& operator<<(std::ostream& os,std::pair<std::string,Values> mypair)
{
auto name{mypair.first};
auto values{mypair.second};
auto dvalue{std::get<0>(values)};
auto bitsetvalue{std::get<1>(values)};
char sign_symbol{bitsetvalue.substr(0,1)=="0"?'+':'-'};
std::bitset<1> sign{bitsetvalue.substr(0,1)};
std::bitset<11> biased_exponent{bitsetvalue.substr(1,11)};
std::bitset<52> mantissa{bitsetvalue.substr(12,52)};
auto mantissa_value{mantissa.to_ullong()};
double mantissa_value_double{static_cast<double>(mantissa_value)};
auto biased_exponent_value{static_cast<signed long long>(biased_exponent.to_ulong())};
bool denormal{biased_exponent_value==0};
std::string denormal_text{denormal?"denormal":""};
signed long long exponent_value{denormal?-1022:biased_exponent_value-1023};
std::string mantissa_with_leading_digit{std::string((denormal?"0.":"1.")) + mantissa.to_string()};
double mantissa_with_leading_digit_value{double{denormal?0.0F:1.0F}+(mantissa_value_double * std::pow(2.0F,-52.0F))};
std::bitset<11> unbiased_exponent{static_cast<unsigned long long>(std::abs(exponent_value))};
char exponent_sign_symbol{exponent_value<0?'-':' '};
std::cout << std::setw(60) << name << std::setw(20) << dvalue << '\n';
std::cout << std::setw(60) << "Binary (biased exponent, hidden leading binary digit)" << std::setw(30) << sign << std::setw(15) << biased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa << '\n';
std::cout << std::setw(60) << "Binary (unbiased exponent, visible leading binary digit)" << std::setw(30) << sign << std::setw(4) << exponent_sign_symbol << unbiased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit << '\n';
std::cout << std::setw(60) << "Decimal (biased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << biased_exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(60) << "Decimal (unbiased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(50) << mantissa_with_leading_digit_value << " * 2**" << std::setw(5) << exponent_value << " = " << std::setw(12) << mantissa_with_leading_digit_value*std::pow(2.0F,exponent_value) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ') << "\n\n\n";
return os;
}
int main()
{
double max_double = std::numeric_limits<double>::max();
double lowest_double = std::numeric_limits<double>::min();
double stored_value{0.4543543234343654632452452525254e-323};
MyMap values{
{"Lowest",std::make_tuple(lowest_double, convert_to_string(lowest_double))},
{"Highest",std::make_tuple(max_double, convert_to_string(max_double))},
{"0.4543543234343654632452452525254e-323",std::make_tuple(stored_value, convert_to_string(stored_value))}
};
std::cout << std::setw(60) << "Variable name" << std::setw(20) << "Decimal value" << std::setw(10) << "Sign" << std::setw(15) << "Exponent" << std::setw(10) << "Exp. Rule" << std::setw(60) << "Mantissa" << std::setw(30) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ');
for (auto& i : values)
std::cout << i;
return 0;
}
输出:
Variable name Decimal value Sign Exponent Exp. Rule Mantissa
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Lowest 2.22507e-308
Binary (biased exponent, hidden leading binary digit) 0 00000000001 0000000000000000000000000000000000000000000000000000
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 1.0000000000000000000000000000000000000000000000000000
Decimal (biased exponent) + 1 1
Decimal (unbiased exponent) + -1022 1
1 * 2**-1022 = 2.22507e-308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Highest 1.79769e+308
Binary (biased exponent, hidden leading binary digit) 0 11111111110 1111111111111111111111111111111111111111111111111111
Binary (unbiased exponent, visible leading binary digit) 0 01111111111 1.1111111111111111111111111111111111111111111111111111
Decimal (biased exponent) + 2046 2
Decimal (unbiased exponent) + 1023 2
2 * 2** 1023 = 1.79769e+308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.4543543234343654632452452525254e-323 4.94066e-324
Binary (biased exponent, hidden leading binary digit) 0 00000000000 denormal 0000000000000000000000000000000000000000000000000001
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 denormal 0.0000000000000000000000000000000000000000000000000001
Decimal (biased exponent) + 0 denormal 2.22045e-16
Decimal (unbiased exponent) + -1022 denormal 2.22045e-16
2.22045e-16 * 2**-1022 = 4.94066e-324
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我把0.4543543234343654632452452525254e-323
赋值给一个double
变量a
打印出来,虽然小于DBL_MIN
,还是可以赋值打印出来的。
DBL_MAX: 1.79769e+308
FLT_MAX: 3.40282e+38
DBL_MIN: 2.22507e-308
FLT_MIN: 1.17549e-38
a: 4.94066e-324
为什么会这样?
实际上DBL_MIN
不是最小值而是可表示的最小值normalized value。
不同之处在于,规范化值的前导数字为 1,非规范化数字的前导数字为 0。请注意,denormal numbers 可能会遇到无法在硬件中管理浮动处理单元的硬件的严重性能问题。
但是您的值 0.454354e-323
对应于 4.545354e-324
小于用 double
表示的最小非正规数,实际上它四舍五入为 4.94066e-324
是可以存储在 double
.
直接查看位更容易理解 IEEE 754-1985 行为。
following program 显示每个数字的符号、尾数和指数以及每个数字的位模式。
#include <iostream>
#include <limits>
#include <iomanip>
#include <bitset>
#include <unordered_map>
#include <string>
#include <tuple>
#include <sstream>
#include <cmath>
using Values = std::tuple<double,std::string>;
using MyMap = std::unordered_map<std::string,Values>;
std::string convert_to_string(double val)
{
auto ptr{reinterpret_cast<const unsigned long long*>(&val)};
auto ival{*ptr};
unsigned long long mask{1ULL << 63};
std::string bitstring;
for (size_t i{0}; i<64; ++i) {
auto bitval{(ival&mask)>0};
mask >>= 1;
bitval? bitstring.push_back('1') : bitstring.push_back('0');
}
return bitstring;
}
std::ostream& operator<<(std::ostream& os,std::pair<std::string,Values> mypair)
{
auto name{mypair.first};
auto values{mypair.second};
auto dvalue{std::get<0>(values)};
auto bitsetvalue{std::get<1>(values)};
char sign_symbol{bitsetvalue.substr(0,1)=="0"?'+':'-'};
std::bitset<1> sign{bitsetvalue.substr(0,1)};
std::bitset<11> biased_exponent{bitsetvalue.substr(1,11)};
std::bitset<52> mantissa{bitsetvalue.substr(12,52)};
auto mantissa_value{mantissa.to_ullong()};
double mantissa_value_double{static_cast<double>(mantissa_value)};
auto biased_exponent_value{static_cast<signed long long>(biased_exponent.to_ulong())};
bool denormal{biased_exponent_value==0};
std::string denormal_text{denormal?"denormal":""};
signed long long exponent_value{denormal?-1022:biased_exponent_value-1023};
std::string mantissa_with_leading_digit{std::string((denormal?"0.":"1.")) + mantissa.to_string()};
double mantissa_with_leading_digit_value{double{denormal?0.0F:1.0F}+(mantissa_value_double * std::pow(2.0F,-52.0F))};
std::bitset<11> unbiased_exponent{static_cast<unsigned long long>(std::abs(exponent_value))};
char exponent_sign_symbol{exponent_value<0?'-':' '};
std::cout << std::setw(60) << name << std::setw(20) << dvalue << '\n';
std::cout << std::setw(60) << "Binary (biased exponent, hidden leading binary digit)" << std::setw(30) << sign << std::setw(15) << biased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa << '\n';
std::cout << std::setw(60) << "Binary (unbiased exponent, visible leading binary digit)" << std::setw(30) << sign << std::setw(4) << exponent_sign_symbol << unbiased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit << '\n';
std::cout << std::setw(60) << "Decimal (biased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << biased_exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(60) << "Decimal (unbiased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(50) << mantissa_with_leading_digit_value << " * 2**" << std::setw(5) << exponent_value << " = " << std::setw(12) << mantissa_with_leading_digit_value*std::pow(2.0F,exponent_value) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ') << "\n\n\n";
return os;
}
int main()
{
double max_double = std::numeric_limits<double>::max();
double lowest_double = std::numeric_limits<double>::min();
double stored_value{0.4543543234343654632452452525254e-323};
MyMap values{
{"Lowest",std::make_tuple(lowest_double, convert_to_string(lowest_double))},
{"Highest",std::make_tuple(max_double, convert_to_string(max_double))},
{"0.4543543234343654632452452525254e-323",std::make_tuple(stored_value, convert_to_string(stored_value))}
};
std::cout << std::setw(60) << "Variable name" << std::setw(20) << "Decimal value" << std::setw(10) << "Sign" << std::setw(15) << "Exponent" << std::setw(10) << "Exp. Rule" << std::setw(60) << "Mantissa" << std::setw(30) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ');
for (auto& i : values)
std::cout << i;
return 0;
}
输出:
Variable name Decimal value Sign Exponent Exp. Rule Mantissa
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Lowest 2.22507e-308
Binary (biased exponent, hidden leading binary digit) 0 00000000001 0000000000000000000000000000000000000000000000000000
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 1.0000000000000000000000000000000000000000000000000000
Decimal (biased exponent) + 1 1
Decimal (unbiased exponent) + -1022 1
1 * 2**-1022 = 2.22507e-308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Highest 1.79769e+308
Binary (biased exponent, hidden leading binary digit) 0 11111111110 1111111111111111111111111111111111111111111111111111
Binary (unbiased exponent, visible leading binary digit) 0 01111111111 1.1111111111111111111111111111111111111111111111111111
Decimal (biased exponent) + 2046 2
Decimal (unbiased exponent) + 1023 2
2 * 2** 1023 = 1.79769e+308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.4543543234343654632452452525254e-323 4.94066e-324
Binary (biased exponent, hidden leading binary digit) 0 00000000000 denormal 0000000000000000000000000000000000000000000000000001
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 denormal 0.0000000000000000000000000000000000000000000000000001
Decimal (biased exponent) + 0 denormal 2.22045e-16
Decimal (unbiased exponent) + -1022 denormal 2.22045e-16
2.22045e-16 * 2**-1022 = 4.94066e-324
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------