code-distortion / realnum
具有本地化格式的任意精度浮点数数学 - 集成Laravel或独立使用
Requires
- php: 7.1.* | 7.2.* | 7.3.* | 7.4.* | 8.0.* | 8.1.* | 8.2.* | 8.3.*
- ext-bcmath: *
- ext-intl: *
- code-distortion/options: ^0.5.8
Requires (Dev)
- jchook/phpunit-assert-throws: ^1.0
- orchestra/testbench: ^3.2 | ^4.0 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0
- phpstan/phpstan: ^0.9 | ^0.10 | ^0.11 | ^0.12 | ^1.0
- phpunit/phpunit: ~4.8 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0
- squizlabs/php_codesniffer: ^3.10
README
code-distortion/realnum是一个PHP库,用于具有本地化格式的任意精度浮点数数学。它集成于Laravel 5 - 9,但也可以独立使用。
以下是一个你可能需要任意精度计算的例子
// an example of floating-point inaccuracy var_dump(0.1 + 0.2 == 0.3); // bool(false) // for more details see: // The Floating-Point Guide - https://floating-point-gui.de/
除了RealNum类外,还有Percent类可供使用,它执行与RealNum相同的所有操作,但针对百分比值。有关更多详细信息,请参阅下面的百分比值部分。
如果你想要处理货币值,请考虑code-distortion/currency包。
安装
使用composer安装此包
composer require code-distortion/realnum
用法
实例化一个RealNum对象,然后你可以开始使用它进行计算、比较,并将其渲染为可读的字符串
use CodeDistortion\RealNum\RealNum; $num1 = new RealNum(5555.55); // normal instantiation $num1 = RealNum::new(5555.55); // static instantiation which is more readable when chaining $num2 = $num1->add(4444.44); // (it's immutable so a new object is created) $num2->between(8000, 10000); // true print $num2->format(); // "9,999.99"
设置值
你可以显式设置值
$num1 = RealNum::new(5); // the value is set to 5 upon instantiation $num2 = $num1->val(10); // and is then set to 10 (it's immutable so a new object is created)
你可以传递给RealNum的类型值有
$num1 = RealNum::new(5); // an integer $num2 = RealNum::new(5.5); // a float $num3 = RealNum::new('6.789'); // a numeric string $num4 = RealNum::new($num3); // another RealNum object $num5 = RealNum::new(null); // null $num6 = RealNum::new(); // (will default to null)
提示:为了保持精度,传递时请使用字符串而不是浮点数
RealNum::new(0.12345678901234567890); // "0.12345678901235" (precision lost because the number passed is a PHP float) RealNum::new('0.12345678901234567890'); // "0.12345678901234567890" (passed as a string)
你还可以设置RealNum使用的其他设置
RealNum::new()->locale('en-US'); // sets the locale this object uses (see the 'locale' section below) RealNum::new()->maxDecPl(30); // sets the maximum number of decimal places used (see the 'precision (maximum decimal places)' section below) RealNum::new()->immutable(false); // sets whether this object is immutable or not (see the 'immutability' section below) RealNum::new()->formatSettings('!thousands'); // alters the default options used when format() is called (see the 'formatting output' section below)
获取值
要获取RealNum中的值,你可以读取val
和cast
属性。val
属性保持精度,而cast
将丢失一些精度,所以根据你的需求使用它们。
$num = RealNum::new('0.12345678901234567890'); print $num->val; // "0.12345678901234567890" (returned as a string, or null) print $num->cast; // 0.12345678901235 (cast to either an integer, float or null - this is less accurate)
你还可以读取RealNum使用的其他设置
$num = RealNum::new(); print $num->locale; // "en" print $num->maxDecPl; // 20 (the maximum number of decimal places used) print $num->immutable; // true var_dump($num->formatSettings); // ['null' => null, 'trailZeros' => null … ]
注意:有关如何将值渲染为可读字符串的更多详细信息,请参阅下面的格式化输出部分。
计算
可用的计算有
$num = RealNum::new(5); $num = $num->inc(); // increment $num = $num->dec(); // decrement $num = $num->add(2); // add x $num = $num->sub(2); // subtract x $num = $num->div(2); // divide by x $num = $num->mul(2); // multiply by x $num = $num->round(); // round to zero decimal places $num = $num->round(2); // round to x decimal places $num = $num->floor(); // use the floor of the current value $num = $num->ceil(); // use the ceiling of the current value
add()
、sub()
、div()
和mul()
方法接受多个值
RealNum::new(5)->add(4, 3, 2, 1); // 15 RealNum::new(5)->sub(4, 3, 2, 1); // -5 RealNum::new(5)->div(4, 3, 2, 1); // 0.2083333… RealNum::new(5)->mul(4, 3, 2, 1); // 120
可以传递的值类型有整数、浮点数、数值字符串和null,以及其他RealNum对象
$num1 = RealNum::new(5); $num1 = $num1->add(2); // pass an integer $num1 = $num1->add(2.0); // pass a float $num1 = $num1->add('2.345'); // pass a numeric string $num1 = $num1->add(null); // pass null (adds nothing) $num2 = RealNum::new(2); $num1 = $num1->add($num2); // pass another RealNum object
比较
你可以使用边界检查来将数字与其它值进行比较
RealNum::new(5)->lessThan(10); // alias of lt(..) RealNum::new(5)->lessThanOrEqualTo(10); // alias of lte(..) RealNum::new(5)->equalTo(10); // alias of eq(..) RealNum::new(5)->greaterThanOrEqualTo(10); // alias of gte(..) RealNum::new(5)->greaterThan(10); // alias of gt(..) $num1 = RealNum::new(5); $num2 = RealNum::new(10); $num1->lt($num2); // you can compare a RealNum with others
你可以将这些比较方法传递多个值。例如。
RealNum::new(5)->lt(10, 15, 20); // will return true if 5 is less-than 10, 15 and 20
你可以检查RealNum的值是否在给定的范围内
RealNum::new(5)->between(2, 8); // check if 5 is between x and y (inclusively) RealNum::new(5)->between(2, 8, false); // check if 5 is between x and y (NOT inclusively)
你可以检查值是否为null
RealNum::new(5)->isNull();
格式化输出
使用format()
方法生成当前值的可读字符串版本
$num = RealNum::new(1234567.89); print $num->format(); // "1,234,567.89"
你可以通过传递选项来改变format()
的输出方式。你可以更改的选项有
null=x
、trailZeros
、decPl=x
、thousands
、showPlus
、accountingNeg
、locale=x
和breaking
。
布尔选项(没有等号的选项)可以通过在其前面添加!
来否定。
注意: format()
选项是通过code-distortion/options包处理的,因此它们可以作为表达式字符串或关联数组传递。
print RealNum::new(null)->format('null=null'); // null (actual null - default) print RealNum::new(null)->format('null="null"'); // "null" (returned as a string) print RealNum::new(null)->format('null=0'); // "0" print RealNum::new(1.23)->maxDecPl(5)->format('!trailZeros'); // "1.23" (cuts off trailing decimal 0's - default) print RealNum::new(1.23)->maxDecPl(5)->format('trailZeros'); // "1.23000" (shows the maximum available decimal-places) // the number can be rounded and shown to a specific number of decimal places (this is different to the internal maxDecPl setting) print RealNum::new(1.9876)->format('decPl=null'); // "1.9876" (no rounding - default) print RealNum::new(1.9876)->format('decPl=0'); // "2" (rounded and shown to 0 decimal places) print RealNum::new(1.9876)->format('decPl=1'); // "2.0" (rounded and shown to 1 decimal place) print RealNum::new(1.9876)->format('decPl=2'); // "1.99" (rounded and shown to 2 decimal places) print RealNum::new(1.9876)->format('decPl=6'); // "1.987600" (rounded and shown to 6 decimal places) // the extra trailing zeros can be removed again with !trailZeros print RealNum::new(1.9876)->format('decPl=6 !trailZeros'); // "1.9876" (rounded to 6 decimal places with the trailing zeros removed) print RealNum::new(1234567.89)->format('thousands'); // "1,234,567.89" (default) print RealNum::new(1234567.89)->format('!thousands'); // "1234567.89" (removes the thousands separator) print RealNum::new(1234)->format('showPlus'); // "+1,234" (adds a '+' for positive values) print RealNum::new(1234)->format('!showPlus'); // "1,234" (default) print RealNum::new(-1234)->format('accountingNeg'); // "(1,234)" (accounting negative - uses brackets for negative numbers) print RealNum::new(-1234)->format('!accountingNeg'); // "-1,234" (default) // the locale can be chosen at the time of formatting - see the 'local' section below for more details print RealNum::new(1234567.89)->format('locale=en'); // "1,234,567.89" (English - default) print RealNum::new(1234567.89)->format('locale=en-AU'); // "1,234,567.89" (Australian English) print RealNum::new(1234567.89)->format('locale=en-IN'); // "12,34,567.89" (Indian English) print RealNum::new(1234567.89)->format('locale=de'); // "1.234.567,89" (German) print RealNum::new(1234567.89)->format('locale=sv'); // "1 234 567,89" (Swedish) print RealNum::new(1234567.89)->format('locale=ar'); // "١٬٢٣٤٬٥٦٧٫٨٩" (Arabic) // non-breaking spaces can be returned instead of regular spaces - see the 'non-breaking whitespace' section below for more details print htmlentities(RealNum::new(1234567.89)->format('locale=sv-SE !breaking')); // "1 234 567,89" (default) print htmlentities(RealNum::new(1234567.89)->format('locale=sv-SE breaking')); // "1 234 567,89" (regular spaces)
可以同时使用多个设置
print RealNum::new(1234567.89)->format('!thousands showPlus locale=de-DE'); // "+1234567,89"
将RealNum转换为字符串与调用不带参数的format()
相同
print (string) RealNum::new(1234567.89); // "1,234,567.89"
注意:RealNum使用PHP的NumberFormatter来渲染可读的输出,但目前存在一个限制,即只能显示大约17位数字(包括小数点前的数字)。因此,如果数字太多,format()
的输出将有点奇怪。但是,内部存储的数字将保持其全部精度。您可以通过读取val
属性来访问完整的数字(参见上面的检索值部分)。
默认格式设置
当调用format()
时,RealNum使用以下默认设置:"null=null !trailZeros decPl=null thousands !showPlus !accountingNeg locale=en !breaking"
注意:在使用Laravel时,您可以在包配置文件中更改此设置。请参阅下面的Laravel部分。
注意: format()
选项是通过code-distortion/options包处理的,因此它们可以作为表达式字符串或关联数组传递。
这些可以根据对象进行调整
$num1 = RealNum::new(1234567.89)->formatSettings('!thousands showPlus'); print $num1->format(); // "+1234567.89" (no thousands separator, show-plus)
默认格式设置可以进行调整。所有新的RealNum对象将以此设置开始
var_dump(RealNum::getDefaultFormatSettings()); // ['null' => null, 'trailZeros' => false … ] (default) RealNum::setDefaultFormatSettings('null="NULL" trailZeros'); var_dump(RealNum::getDefaultFormatSettings()); // ['null' => 'NULL', 'trailZeros' => true … ]
区域设置
注意:在使用Laravel时,这将被自动设置。请参阅下面的Laravel部分。
RealNum的默认区域设置是"en"(英语),但您可以选择使用哪个。
您可以在格式化时选择区域设置
print RealNum::new(1234567.89)->format('locale=fr-FR'); // "1 234 567,89"
您可以根据对象更改区域设置
$num1 = RealNum::new(1234567.89)->locale('fr-FR'); // (it's immutable so a new object is created) print $num1->locale; // "fr-FR" print $num1->format(); // "1 234 567,89"
默认区域设置可以更改。所有新的RealNum对象将以此设置开始
RealNum::setDefaultLocale('fr-FR'); print RealNum::getDefaultLocale(); // "fr-FR"
精度(最大小数位数)
注意:在使用Laravel时,您可以在包配置文件中更改此设置。请参阅下面的Laravel部分。
maxDecPl
精度设置是RealNum可以处理的最大小数位数。默认使用20位maxDecPl,但您可以根据对象更改此设置。
$num = RealNum::new('0.123456789012345678901234567890'); // passed as a string to maintain precision print $num->val; // "0.12345678901234567890" ie. rounded to the default 20 decimal places $num = RealNum::new()->maxDecPl(30)->val('0.123456789012345678901234567890'); print $num->val; // "0.123456789012345678901234567890" the full 30 decimal places
默认精度可以更改。所有新的RealNum对象将以此设置开始
RealNum::setDefaultMaxDecPl(30); print RealNum::getDefaultMaxDecPl(); // 30
不可变性
注意:在使用Laravel时,您可以在包配置文件中更改此设置。请参阅下面的Laravel部分。
RealNum默认是不可变的,这意味着一旦创建对象,它就不会改变。任何更改其内容的东西都将返回一个新的RealNum。这样,您可以向代码的其他部分传递RealNum对象,并确信它不会意外更改
$num1 = RealNum::new(1); $num2 = $num1->add(2); // $num1 remains unchanged and $num2 is a new object containing the new value print $num1->format(); // "1" print $num2->format(); // "3"
可以针对对象关闭不可变性
$num1 = RealNum::new(1)->immutable(false); $num2 = $num1->add(2); // $num1 is changed and $num2 points to the same object print $num1->format(); // "3" print $num2->format(); // "3"
可以默认关闭不可变性。所有新的RealNum对象将以此设置开始
RealNum::setDefaultImmutability(false); var_dump(RealNum::getDefaultImmutability()); // "bool(false)"
您可以显式创建RealNum对象的副本
$num1 = RealNum::new(); $num2 = $num1->copy(); // this will return a clone regardless of the immutability setting
非断行空白
一些区域设置在渲染数字时使用空格(例如,瑞典使用空格作为千位分隔符)。format()
可以返回包含非断行空白字符或常规空格字符的字符串。
非断行空白的例子是UTF-8的\xc2\xa0
字符,它被用来替代常规的\x20
空格字符。还有其他一些,如\xe2\x80\xaf
,它是一个'窄非断行空格'。
当转换为html实体时,\xc2\xa0
UTF-8字符将成为熟悉的
。
因为format()
是为了产生供人类阅读的数字而设计的,所以RealNum默认使用非断行空白,但您可以指示它返回常规空格
$num = RealNum::new(1234567.89)->locale('sv-SE'); // Swedish print htmlentities($num->format('!breaking')); // "1 234 567,89" (contains non-breaking whitespace - default) print htmlentities($num->format('breaking')); // "1 234 567,89" (regular spaces)
提示:非断行空白设置可以针对对象和默认设置进行更改。请参阅上面的格式化输出和默认格式设置部分。
链式调用
上面的设置和计算方法可以链式调用。例如。
print RealNum::new(1) ->locale('en-US')->val(5)->maxDecPl(3) // some "setting" methods ->add(4)->mul(3)->div(2)->sub(1) // some "calculation" methods ->format(); // "12.5"
百分比值
虽然RealNum
类用于常规浮点数,但您可以使用Percent
类执行与RealNum相同的所有操作,但用于百分比值。区别在于传递给Percent的值,并且其format()
输出将显示百分比符号。
use CodeDistortion\RealNum\Percent; $percent = new Percent(1); // normal instantiation $percent = Percent::new(1); // static instantiation which is more readable when chaining print Percent::new(0)->format(); // "0%" print Percent::new(1)->format(); // "100%" (note that 1 was passed, not 100) print Percent::new(0.5)->format(); // "50%" print Percent::new(0.01)->format(); // "1%" print Percent::new(100)->format(); // "10,000%" (this happens if you pass 100) print Percent::new(-1)->format(); // "-100%" print Percent::new(null)->format(); // null
您还可以将Percent对象与RealNum一起使用
$num = RealNum::new(20); $percent = Percent::new(0.5); // 50% print $num->mul($percent); // 10
Laravel
RealNum包是框架无关的,可以独立使用,但它也与Laravel 5、6、7、8 & 9集成。
服务提供者
RealNum 与 Laravel 5.5+ 自动集成,得益于 Laravel 的包自动检测功能。
Laravel 的区域设置已与 RealNum 和 Percent 注册,并在更改后更新。
(点击此处获取 Laravel <= 5.4 的信息)
对于 Laravel 5.0 - 5.4,请将以下行添加到 config/app.php
'providers' => [ … CodeDistortion\RealNum\Laravel\ServiceProvider::class, … ],
配置
您可以通过发布 config/code-distortion.realnum.php 配置文件并更新它来指定默认的 max-dec-pl、不可变性和格式设置
php artisan vendor:publish --provider="CodeDistortion\RealNum\Laravel\ServiceProvider" --tag="config"
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志
语义化版本
此库使用 SemVer 2.0.0 版本控制。这意味着对 X
的更改表示破坏性更改:0.0.X
、0.X.y
、X.y.z
。当此库升级到 1.0.0、2.0.0 等版本时,并不表示它是一个重要的发布版本,它只是表示更改是破坏性的。
实物捐赠
此包是 实物捐赠。如果您在生产环境中使用它,我们请求您为世界种一棵树以感谢我们的工作。通过为实物捐赠森林做出贡献,您将为当地家庭创造就业机会并恢复野生动物栖息地。
贡献
有关详细信息,请参阅 贡献指南
行为准则
有关详细信息,请参阅 行为准则
安全性
如果您发现任何与安全相关的问题,请通过电子邮件 tim@code-distortion.net 联系,而不是使用问题跟踪器。
致谢
许可证
MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件