C/C++标准库由于需要具有良好的通用性与全面性,使用了较为复杂的实现方法,实现的代码庞大
对于简单的Windows程序,使用C/C++标准库生成的EXE文件体积较大,整个程序80%以上代码可能均来源于标准库
一种可行的解决办法是使用C/C++动态库(MTd、MT),但这要求系统安装有相应版本的CRT
简单的程序往往功能要求不多、限制通常较少、无需考虑过多情况,使用C/C++标准库如同使用高射炮打蚊子
此时,一种更好的方法是架空C/C++标准库,直接使用Windows API (参看: C/C++程序减小可执行文件的大小)
常用的标准库函数实现往往比较简单,但浮点数与字符串的转换则较为复杂,自己实现的代码往往无法周到全面地考虑各种情况
笔者通过对浮点数存储格式深入研究,并参考VS2015标准库相关代码,实现了不使用C/C++标准库浮点数与字符串相互转换的代码
代码考虑了字符串各种特殊情况:Inf、Nan、上溢、下溢等等,支持十六进制的浮点数字符串,运行效果与使用标准库完全相同
啰嗦了那么多,言归正传,直接给实现代码
头文件(浮点数转字符串与字符串转浮点数的函数接口):
/
// DoubleConversion.h: header file
//
// DoubleConversion - Do not use CRT.
//
// Written by Paschen <paschen@sohu.com>
// Copyright (c) 2017. All Rights Reserved.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name and all copyright
// notices remains intact.
//
// An email letting me know how you are using it would be nice as well.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage/loss of business that
// this product may cause.
//
/
#ifndef DOUBLECONVERSION_H_H_H
#define DOUBLECONVERSION_H_H_H
#include <Windows.h>
//说明: 将double类型浮点数转换为字符串
//参数:
// lpszBuffer: 转换结果缓冲区
// value: 要转换的浮点数
// FractionalDigitCount: 保留的小数位数
//返回值: 转换结果(字符串)
LPTSTR Double2String(LPTSTR lpszBuffer, double value, UINT32 FractionalDigitCount);
//转换状态枚举
typedef enum
{
SLD_OK,
SLD_NODIGITS,
SLD_UNDERFLOW,
SLD_OVERFLOW
} SLD_STATUS;
//说明: 将字符串转换为double类型浮点数
//参数:
// lpszString: 要转换的字符串
// result: 转换结果(double类型浮点数)
//返回值: 转换状态枚举
SLD_STATUS String2Double(LPCTSTR lpszString, double& result);
#endif
源文件(函数具体实现,由于实现过程复杂,可不必仔细研读,包含使用即可):
/
// DoubleConversion.cpp : implementation file
//
// DoubleConversion - Do not use CRT.
//
// Written by Paschen <paschen@sohu.com>
// Copyright (c) 2017. All Rights Reserved.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name and all copyright
// notices remains intact.
//
// An email letting me know how you are using it would be nice as well.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage/loss of business that
// this product may cause.
//
/
#include "DoubleConversion.h"
#include <Windows.h>
#define CHAR_BIT 8
#define DBL_MANT_DIG 53I32
#define DBL_MAX_EXP 1024I32
#define DBL_MIN_EXP (-1021I32)
#define LN10 2.3025850929940456840179914547
#define LN1_2217 0.2002433314278771112016301167
#define __MAX(a,b) (((a) > (b)) ? (a) : (b))
#define __MIN(a,b) (((a) < (b)) ? (a) : (b))
#ifndef _countof
#ifdef __cplusplus
extern "C++"
{
template <typename _CountofType, size_t _SizeOfArray>
char(*__countof_helper(_UNALIGNED _CountofType(&_Array)[_SizeOfArray]))[_SizeOfArray];
#define _countof(_Array) (sizeof(*__countof_helper(_Array)) + 0U)
}
#else
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#endif
#endif
enum : INT32
{
mantissa_bits = DBL_MANT_DIG,
exponent_bits = (INT32)sizeof(double) * (INT32)CHAR_BIT - DBL_MANT_DIG,
maximum_binary_exponent = DBL_MAX_EXP - 1I32,
minimum_binary_exponent = DBL_MIN_EXP - 1I32,
exponent_bias = 1023I32
};
enum : UINT64
{
exponent_mask = (1UI64 << (exponent_bits)) - 1UI64,
normal_mantissa_mask = (1UI64 << (mantissa_bits)) - 1UI64,
denormal_mantissa_mask = (1UI64 << (mantissa_bits - 1UI64)) - 1UI64,
special_nan_mantissa_mask = (1UI64 << (mantissa_bits - 2UI64))
};
enum : INT32
{
maximum_temporary_decimal_exponent = 5200I32,
minimum_temporary_decimal_exponent = -5200I32
};
struct double_type
{
UINT64 _mantissa : mantissa_bits - 1I32;
UINT64 _exponent : exponent_bits;
UINT64 _sign : 1I32;
};
#pragma optimize( "", off )
static inline VOID Memcpy(LPVOID lpDst, LPCVOID lpSrc, SIZE_T dwLen)
{
SIZE_T n = dwLen & ~(sizeof(UINT64) - 1U);
PUINT64 pDst64 = (PUINT64)lpDst;
PUINT64 pSrc64 = (PUINT64)lpSrc;
while (n)
{
*pDst64++ = *pSrc64++;
n -= sizeof(UINT64);
}
n = dwLen & (sizeof(UINT64) - 1U);
PUINT8 pDst8 = (PUINT8)pDst64;
PUINT8 pSrc8 = (PUINT8)pSrc64;
while (n--)
*pDst8++ = *pSrc8++;
}
#pragma optimize( "", on )
#ifdef _DEBUG
#pragma optimize( "", off )
static inline VOID Memset(LPVOID lpDst, int Val, SIZE_T dwLen)
{
PUINT8 pDst8 = (PUINT8)lpDst;
while (dwLen--)
*pDst8++ = (UINT8)Val;
}
#pragma optimize( "", on )
#endif
static inline VOID Memsetzero(LPVOID lpDst, SIZE_T dwLen)
{
SIZE_T n = dwLen & ~(sizeof(UINT64) - 1U);
PUINT64 pDst64 = (PUINT64)lpDst;
while (n)
{
*pDst64++ = (UINT64)0U;
n -= sizeof(UINT64);
}
n = dwLen & (sizeof(UINT64) - 1U);
PUINT8 pDst8 = (PUINT8)pDst64;
while (n--)
*pDst8++ = (UINT8)0U;
}
static inline double Log(double x)
{
int k = 0, l = 0;
for (; x > 1; ++k)
x /= 10.0;
for (; x <= 0.1; --k)
x *= 10.0;
for (; x < 0.9047; --l)
x *= 1.2217;
double y = (x - 1.0) / (x + 1.0);
double y2 = y * y;
double t = y2;
double z = t / 3.0;
double v = 1.0;
for (UINT32 i = 3UI32; z; z = (t *= y2) / (double)(i += 2UI32))
v += z;
return k * LN10 + l * LN1_2217 + v * y * 2;
}
static inline INT32 Ceil(double x)
{
double_type& components = (double_type&)x;
if (!components._exponent && !components._mantissa)
return 0I32;
if (components._sign)
return (INT32)x;
return (INT32)x + 1I32;
}
struct big_integer
{
big_integer() : used(0UI32)
{
#ifdef _DEBUG
Memset(data, 0xcc, sizeof(data));
#endif
}
big_integer(const big_integer& other) : used(other.used)
{
Memcpy(data, other.data, other.used * sizeof(UINT32));
}
big_integer& operator=(const big_integer& other)
{
used = other.used;
Memcpy(data, other.data, (size_t)other.used * sizeof(UINT32));
return *this;
}
enum : UINT32
{
maximum_bits = 1074UI32 + 2552UI32 + 32UI32, // 1074 bits required to represent 2^1074 | ceil(log2(10^768)) | shift space
element_bits = (UINT32)sizeof(UINT32) * (UINT32)CHAR_BIT,
element_count = (maximum_bits + element_bits - 1UI32) / element_bits
};
UINT32 used;
UINT32 data[element_count];
};
static inline BOOL operator==(const big_integer& lhs, const big_integer& rhs)
{
if (lhs.used != rhs.used)
return FALSE;
for (UINT32 i = 0UI32; i != lhs.used; ++i)
if (lhs.data[i] != rhs.data[i])
return FALSE;
return TRUE;
}
static inline BOOL operator!=(const big_integer& lhs, const big_integer& rhs)
{
return !(rhs == lhs);
}
static inline BOOL operator<(const big_integer& lhs, const big_integer& rhs)
{
if (lhs.used > rhs.used)
return FALSE;
if (lhs.used < rhs.used)
return TRUE;
UINT32 i = lhs.used - 1UI32;
for (; i != (UINT32)(-1) && lhs.data[i] == rhs.data[i]; --i)
{
}
if (i == (UINT32)(-1))
return FALSE;
if (lhs.data[i] <= rhs.data[i])
return TRUE;
return FALSE;
}
static inline BOOL operator>=(const big_integer& lhs, const big_integer& rhs)
{
return !(lhs < rhs);
}
static inline big_integer make_big_integer(UINT64 value)
{
big_integer x;
x.data[0] = value & 0xFFFFFFFFUI64;
x.data[1] = (UINT32)(value >> 32UI64);
x.used = x.data[1] ? 2UI32 : 1UI32;
return x;
}
static inline big_integer make_big_integer_power_of_two(UINT32 power)
{
UINT32 one = 1UI32;
big_integer x;
UINT32 element_index = power / big_integer::element_bits;
UINT32 bit_index = power % big_integer::element_bits;
Memsetzero(x.data, (size_t)element_index * sizeof(UINT32));
x.data[element_index] = (one << bit_index);
x.used = element_index + 1UI32;
return x;
}
static inline BOOL is_zero(const big_integer& value)
{
return value.used == 0UI32;
}
static inline UINT32 bit_scan_reverse(UINT32 value)
{
UINT32 index;
if (_BitScanReverse((LPDWORD)&index, value))
return index + 1UI32;
return 0UI32;
}
static inline UINT32 bit_scan_reverse(UINT64 value)
{
if (value > 0xFFFFFFFFUI64)
return bit_scan_reverse(((PUINT32)(&value))[1]) + 32UI32;
else
return bit_scan_reverse(((PUINT32)(&value))[0]);
}
static inline UINT32 bit_scan_reverse(const big_integer& x)
{
if (x.used == 0UI32)
return 0UI32;
return (x.used - 1UI32) * big_integer::element_bits + bit_scan_reverse(x.data[x.used - 1UI32]);
}
static inline BOOL shift_left(big_integer& x, UINT32 n)
{
UINT32 unit_shift = n / big_integer::element_bits;
UINT32 bit_shift = n % big_integer::element_bits;
UINT64 one = 1UI64;
UINT32 msb_bits = bit_shift;
UINT32 lsb_bits = big_integer::element_bits - msb_bits;
UINT32 lsb_mask = (UINT32)((one << lsb_bits) - one);
UINT32 msb_mask = ~lsb_mask;
BOOL bit_shifts_into_next_unit = bit_shift > (big_integer::element_bits - bit_scan_reverse(x.data[x.used - 1UI32]));
BOOL unit_shift_will_overflow = x.used + unit_shift > big_integer::element_count;
BOOL bit_shift_will_overflow = x.used + unit_shift == big_integer::element_count && bit_shifts_into_next_unit;
if (unit_shift_will_overflow || bit_shift_will_overflow)
{
x = big_integer();
return FALSE;
}
UINT32 max_destination_index = __MIN(x.used + unit_shift, big_integer::element_count - 1UI32);
for (UINT32 destination_index = max_destination_index; destination_index != (UINT32)(-1) && destination_index >= unit_shift; --destination_index)
{
UINT32 upper_source_index = destination_index - unit_shift;
UINT32 lower_source_index = destination_index - unit_shift - 1UI32;
UINT32 upper_source = upper_source_index < x.used ? x.data[upper_source_index] : 0UI32;
UINT32 lower_source = lower_source_index < x.used ? x.data[lower_source_index] : 0UI32;
UINT32 shifted_upper_source = (upper_source & lsb_mask) << msb_bits;
UINT32 shifted_lower_source = (lower_source & msb_mask) >> lsb_bits;
UINT32 combined_shifted_source = shifted_upper_source | shifted_lower_source;
x.data[destination_index] = combined_shifted_source;