* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cm_dec2.c
*
*
* IDENTIFICATION
* src/common/cm_dec2.c
*
* -------------------------------------------------------------------------
*/
#include "cm_dec2.h"
#include "cm_text.h"
#include "cm_binary.h"
#include "var_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define GET_ZEROS_TAIL_CELL(u) (((u) % 10 == 0) ? 1 : 0)
#define GET_DIGITS_HEAD_CELL(u) (((u) >= 10) ? 2 : 1)
#define DEC2_HALF_MASK 50U
#define DEC2_POW2_MASK ((uint32)10000u)
#define DEC2_POW3_MASK ((uint64)1000000u)
#define DEC2_POW4_MASK ((uint64)100000000u)
#define DEC2_POW5_MASK ((uint64)10000000000u)
#define DEC2_POW6_MASK ((uint64)1000000000000u)
#define DEC2_POW7_MASK ((uint64)100000000000000u)
#define DEC2_POW8_MASK ((uint64)10000000000000000u)
#define DEC2_POW9_MASK ((uint64)1000000000000000000u)
static const uint64 g_pow100_u64[] = {
1,
DEC2_CELL_MASK,
DEC2_POW2_MASK,
DEC2_POW3_MASK,
DEC2_POW4_MASK,
DEC2_POW5_MASK,
DEC2_POW6_MASK,
DEC2_POW7_MASK,
DEC2_POW8_MASK,
DEC2_POW9_MASK,
};
static const dec2_t DEC2_HALF_ONE = {
.len = 2,
.head = CONVERT_EXPN(-2, OG_FALSE),
.cells = { 50 }
};
const dec2_t DEC2_ONE = {
.len = 2,
.head = CONVERT_EXPN(0, OG_FALSE),
.cells = { 1 }
};
static const dec2_t DEC2_HALF_PI = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(0, OG_FALSE),
.cells = { 1, 57, 07, 96, 32, 67, 94, 89, 66, 19, 23, 13, 21, 69, 16, 39, 75, 14, 42, 9, 85, 84, 69, 96, 88 }
};
static const dec2_t DEC2_PI = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(0, OG_FALSE),
.cells = { 3, 14, 15, 92, 65, 35, 89, 79, 32, 38, 46, 26, 43, 38, 32, 79, 50, 28, 84, 19, 71, 69, 39, 93, 75 }
};
static const dec2_t DEC2_2PI = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(0, OG_FALSE),
.cells = { 6, 28, 31, 85, 30, 71, 79, 58, 64, 76, 92, 52, 86, 76, 65, 59, 00, 57, 68, 39, 43, 38, 79, 87, 50 }
};
static const dec2_t DEC2_INV_2PI = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-2, OG_FALSE),
.cells = { 15, 91, 54, 94, 30, 91, 89, 53, 35, 76, 88, 83, 76, 33, 72, 51, 43, 62, 03, 44, 59, 64, 57, 40, 46 }
};
const dec2_t DEC2_MIN_INT64 = {
.len = 11,
.head = CONVERT_EXPN(18, OG_TRUE),
.cells = { 9, 22, 33, 72, 03, 68, 54, 77, 58, 8 }
};
static const dec2_t DEC2_MIN_INT32 = {
.len = 6,
.head = CONVERT_EXPN(8, OG_TRUE),
.cells = { 21, 47, 48, 36, 48 }
};
static const dec2_t g_inv_fact[] = {
= 16.6666666666666666666666666666666666666667 * 10^-2 */
[_I(3)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-2, OG_FALSE),
.cells = { 16, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67 }
},
* 0.04166666666666666666666666666666666666666666666666666666666666666666666666666666666666666
* 6666666666666666666666
* 4.166666666666666666666666666666666666666667 * 10^-2 */
[_I(4)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-2, OG_FALSE),
.cells = { 4, 16, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67 }
},
* 83.33333333333333333333333333333333333333333 * 10 ^-4 */
[_I(5)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-4, OG_FALSE),
.cells = { 83, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 }
},
* 0.001388888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
* 8888888888888888888888888888888888888888888888888888888
*/
[_I(6)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-4, OG_FALSE),
.cells = { 13, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 89, 88, 89 }
},
[_I(7)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-4, OG_FALSE),
.cells = { 1, 98, 41, 26, 98, 41, 26, 98, 41, 26, 98, 41, 26, 98, 41, 26, 98, 41, 26, 98, 41, 26, 98, 41, 26 }
},
* 0.0000248015873015873015873015873015873015873015873015873015873015873015873015873015873015873
* 015873015873015873015873015873015873015873015873015873015873015873
*/
[_I(8)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-6, OG_FALSE),
.cells = { 24, 80, 15, 87, 30, 15, 87, 30, 15, 87, 30, 15, 87, 30, 15, 87, 30, 15, 87, 30, 15, 87, 30, 15, 87 }
},
* 7557319223985890652557319223986 */
[_I(9)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-6, OG_FALSE),
.cells = { 2, 75, 57, 31, 92, 23, 98, 58, 90, 65, 25, 57, 31, 92, 23, 98, 58, 90, 65, 25, 57, 31, 92, 23, 99 }
},
* 7557319223985890652557319223986 */
[_I(10)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-8, OG_FALSE),
.cells = { 27, 55, 73, 19, 22, 39, 85, 89, 06, 52, 55, 73, 19, 22, 39, 85, 89, 06, 52, 55, 73, 19, 22, 39, 86 }
},
* 0.000000025052108385441718775052108385441718775052108385441718775052108385441718775
* 0521083854417187750521083854417187750521
*/
[_I(11)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-8, OG_FALSE),
.cells = { 2, 50, 52, 10, 83, 85, 44, 17, 18, 77, 50, 52, 10, 83, 85, 44, 17, 18, 77, 50, 52, 10, 83, 85, 44 }
},
* 0.0000000020876756987868098979210090321201432312543423654534765645876756987868098979210090
* 3212014323125434236545347656458767569878680989792
*/
[_I(12)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-10, OG_FALSE),
.cells = { 20, 87, 67, 56, 98, 78, 68, 9, 89, 79, 21, 00, 90, 32, 12, 01, 43, 23, 12, 54, 34, 23, 65, 45, 34 }
},
* 0.00000000016059043836821614599392377170154947932725710503488281266059043836821614599392377
* 170154947932725710503488281266
*/
[_I(13)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-10, OG_FALSE),
.cells = { 1, 60, 59, 04, 38, 36, 82, 16, 14, 59, 93, 92, 37, 71, 70, 15, 49, 47, 93, 27, 25, 71, 05, 03, 49 }
},
* 0.0000000000114707455977297247138516979786821056662326503596344866186136027405868675709945551215
* 3924852337550750249162947575645988344401042813741226439639138
*/
[_I(14)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-12, OG_FALSE),
.cells = { 11, 47, 07, 45, 59, 77, 29, 72, 47, 13, 85, 16, 97, 97, 86, 82, 10, 56, 66, 23, 26, 50, 35, 96, 34 }
},
* 0.0000000000007647163731819816475901131985788070444155100239756324412409068493724578380663036
* 74769283234891700500166108631717
*/
[_I(15)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-14, OG_FALSE),
.cells = { 76, 47, 16, 37, 31, 81, 98, 16, 47, 59, 01, 13, 19, 85, 78, 80, 70, 44, 41, 55, 10, 02, 39, 75, 63 }
},
* 0.0000000000000477947733238738529743820749111754402759693764984770275775566780857786148791439
* 79673080202180731281260381789482318582847683
*/
[_I(16)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-14, OG_FALSE),
.cells = { 4, 77, 94, 77, 33, 23, 87, 38, 52, 97, 43, 82, 07, 49, 11, 17, 54, 40, 27, 59, 69, 37, 64, 98, 48 }
},
* 0.00000000000000281145725434552076319894558301032001623349273520453103397392224033991852230
* 258703959295306945478125061069
*/
[_I(17)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-16, OG_FALSE),
.cells = { 28, 11, 45, 72, 54, 34, 55, 20, 76, 31, 98, 94, 55, 83, 01, 03, 20, 01, 62, 33, 49, 27, 35, 20, 45 }
},
* 0.00000000000000015619206968586226462216364350057333423519404084469616855410679112999547346
* 125483553294183719193229170059408327555092
*/
[_I(18)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-16, OG_FALSE),
.cells = { 1, 56, 19, 20, 69, 68, 58, 62, 26, 46, 22, 16, 36, 43, 50, 05, 73, 33, 42, 35, 19, 40, 40, 84, 47 }
},
* 0.00000000000000000822063524662432971695598123687228074922073899182611413442667321736818281375
* 025450173378090483854166845232
*/
[_I(19)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-18, OG_FALSE),
.cells = { 8, 22, 06, 35, 24, 66, 24, 32, 97, 16, 95, 59, 81, 23, 68, 72, 28, 07, 49, 22, 07, 38, 99, 18, 26 }
},
* 0.0000000000000000004110317623312164858477990618436140374610369495913057067213336608684091406875
* 1272508668904524192708342261600861987085352324885435075580
*/
[_I(20)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-20, OG_FALSE),
.cells = { 41, 10, 31, 76, 23, 31, 21, 64, 85, 84, 77, 99, 06, 18, 43, 61, 40, 37, 46, 10, 36, 94, 95, 91, 31 }
},
* 0.000000000000000000019572941063391261230847574373505430355287473790062176510539698136590911461
* 31012976603281167818700397250552421999385016777375496908360952830809216
*/
[_I(21)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-20, OG_FALSE),
.cells = { 1, 95, 72, 94, 10, 63, 39, 12, 61, 23, 8, 47, 57, 43, 73, 50, 54, 30, 35, 52, 87, 47, 37, 90, 06 }
},
* 0.00000000000000000000088967913924505732867488974425024683433124880863918984138816809711776870278
* 68240802742187126448638169320692827269931894
*/
[_I(22)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-22, OG_FALSE),
.cells = { 8, 89, 67, 91, 39, 24, 50, 57, 32, 86, 74, 88, 97, 44, 25, 02, 46, 83, 43, 31, 24, 88, 8, 63, 92 }
},
* 0.0000000000000000000000386817017063068403771691193152281232317934264625734713647029607442508131646
* 445252293138570715158181274812731620431821497505038914695840480397078275699598837
*/
[_I(23)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-24, OG_FALSE),
.cells = { 38, 68, 17, 01, 70, 63, 06, 84, 03, 77, 16, 91, 19, 31, 52, 28, 12, 32, 31, 79, 34, 26, 46, 25, 73 }
},
* 0.0000000000000000000000016117375710961183490487133048011718013247261026072279735292900310104505485
* 268552178880773779798257553117197150851
*/
[_I(24)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-24, OG_FALSE),
.cells = { 1, 61, 17, 37, 57, 10, 96, 11, 83, 49, 04, 87, 13, 30, 48, 01, 17, 18, 01, 32, 47, 26, 10, 26, 07 }
},
* 0.0000000000000000000000000644695028438447339619485321920468720529890441042891189411716012404180219
* 410742087155230951191930302124687886034053035829175064857826400800661797126165998
*/
[_I(25)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-26, OG_FALSE),
.cells = { 6, 44, 69, 50, 28, 43, 84, 47, 33, 96, 19, 48, 53, 21, 92, 04, 68, 72, 05, 29, 89, 4, 41, 4, 29 }
},
* 0.00000000000000000000000000247959626322479746007494354584795661742265554247265842081429235540069315
* 15797772582893498122766550081718764847463578301
*/
[_I(26)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-28, OG_FALSE),
.cells = { 24, 79, 59, 62, 63, 22, 47, 97, 46, 00, 74, 94, 35, 45, 84, 79, 56, 61, 74, 22, 65, 55, 42, 47, 26 }
},
* 0.00000000000000000000000000009183689863795546148425716836473913397861687194343179336349230945928493
* 153999175030701295601024648178414357350912436407823006621906358985764413064475299
*/
[_I(27)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-30, OG_FALSE),
.cells = { 91, 83, 68, 98, 63, 79, 55, 46, 14, 84, 25, 71, 68, 36, 47, 39, 13, 39, 78, 61, 68, 71, 94, 34, 31 }
},
* 0.00000000000000000000000000000327988923706983791015204172731211192780774542655113547726758248068874
* 7554999705368107605571794517206576556196754441574222502364966556780630
*/
[_I(28)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-30, OG_FALSE),
.cells = { 3, 27, 98, 89, 23, 70, 69, 83, 79, 10, 15, 20, 41, 72, 73, 12, 11, 19, 27, 80, 77, 45, 42, 42, 66 }
},
* 0.0000000000000000000000000000001130996288644771693155876457693831699244050147086598440437097407134
* 0508810343811614164157144119024850263986885360143359387939189539850967690163872506526007013143054544564
*/
[_I(29)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-32, OG_FALSE),
.cells = { 11, 30, 99, 62, 88, 64, 47, 71, 69, 31, 55, 87, 64, 57, 69, 38, 31, 69, 92, 44, 05, 01, 47, 86, 50 }
},
* 0.000000000000000000000000000000003769987628815905643852921525646105664146833823621994801456991357113
* 502936781270538054719048039674950087995628453381119795979729846616989230
*/
[_I(30)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-34, OG_FALSE),
.cells = { 37, 69, 98, 76, 28, 81, 59, 05, 64, 38, 52, 92, 15, 25, 64, 61, 05, 66, 41, 46, 83, 38, 23, 62, 20 }
},
* 0.000000000000000000000000000000000121612504155351794962997468569229214972478510439419187143773914745
* 59686892842808187273287251740886935767727833720584257406386225311667707193724594093
*/
[_I(31)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-34, OG_FALSE),
.cells = { 1, 21, 61, 25, 04, 15, 53, 51, 79, 49, 62, 99, 74, 68, 56, 92, 29, 21, 49, 72, 47, 85, 10, 43, 94 }
},
* 0.00000000000000000000000000000000000380039075485474359259367089278841296788995345123184959824293483
* 57999021540133775585229022661690271674274149480376825804394956954
*/
[_I(32)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-36, OG_FALSE),
.cells = { 3, 80, 03, 90, 75, 48, 54, 74, 35, 92, 59, 36, 70, 89, 27, 88, 41, 29, 67, 88, 99, 53, 45, 12, 32 }
},
* 0.000000000000000000000000000000000000115163356207719502805868814932982211148180407613086351461907
* 11623636067133373871389463340200512203537658833175871765395271199076999685328781936168648710906456849803
*/
[_I(33)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-38, OG_FALSE),
.cells = { 11, 51, 63, 35, 62, 07, 71, 95, 02, 80, 58, 68, 81, 49, 32, 98, 22, 11, 14, 81, 80, 40, 76, 13, 9 }
},
* 0.00000000000000000000000000000000000000338715753552116184723143573332300621024060022391430445476
* 19740069517844509923151145480412354447657463702450517269898221385879638234368614064518143084443842520
*/
[_I(34)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-40, OG_FALSE),
.cells = { 33, 87, 15, 75, 35, 52, 11, 61, 84, 72, 31, 43, 57, 33, 32, 30, 06, 21, 02, 40, 60, 02, 23, 91, 43 }
},
* 0.000000000000000000000000000000000000000096775929586318909920898163809228748864017149254694412993
* 19925734147955574263757470137260672699330703914985862077113777538822753781248175447
*/
[_I(35)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-42, OG_FALSE),
.cells = { 96, 77, 59, 29, 58, 63, 18, 90, 99, 20, 89, 81, 63, 80, 92, 28, 74, 88, 64, 01, 71, 49, 25, 46, 94 }
},
* 0.0000000000000000000000000000000000000000026882202662866363866916156613674652462226985904081781
* 38699979370596654326184377075038127964638702973309718295021420493760784098272568937624168106594
*/
[_I(36)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-42, OG_FALSE),
.cells = { 2, 68, 82, 20, 26, 62, 86, 63, 63, 86, 69, 16, 15, 66, 13, 67, 46, 52, 46, 22, 26, 98, 59, 04, 82 }
},
* 0.0000000000000000000000000000000000000000000726546017915307131538274503072287904384513132542750
* 848297291721782879547617399209469764314767217019813437377032816349665
*/
[_I(37)] = {
.len = DEC2_MAX_LEN,
.head = CONVERT_EXPN(-44, OG_FALSE),
.cells = { 7, 26, 54, 60, 17, 91, 53, 07, 13, 15, 38, 27, 45, 03, 07, 22, 87, 90, 43, 84, 51, 31, 32, 54, 26 }
}
};
static inline bool32 cm_dec2_taylor_break(const dec2_t *total, const dec2_t *delta, int32 prec)
{
if (DECIMAL2_IS_ZERO(delta)) {
return OG_TRUE;
}
int32 total_expn = GET_100_EXPN(total);
int32 delta_expn = GET_100_EXPN(delta);
if (total_expn + (((total)->cells[0] >= 10) ? 1 : 0) >= (int32)SEXP_2_D2EXP(prec) + delta_expn) {
return OG_TRUE;
}
return OG_FALSE;
}
static inline void cm_dec2_left_shift(const dec2_t *dec, uint32 offset, dec2_t *rs)
{
uint32 ri;
uint32 di;
for (ri = 0, di = offset; di < GET_CELLS_SIZE(dec); ++ri, ++di) {
rs->cells[ri] = dec->cells[di];
}
rs->len = (uint8)((uint32)dec->len - offset);
if (SECUREC_UNLIKELY((int32)GET_100_EXPN(dec) - (int32)offset < DEC2_EXPN_LOW_HALF)) {
cm_zero_dec2(rs);
return;
}
rs->head = CONVERT_EXPN2(GET_100_EXPN(dec) - offset, IS_DEC_NEG(dec));
}
* Right shift decimal cells. The leading cells are filled with zero.
* @note The following conditions should be guaranteed by caller
* + offset > 0 and offset < DEC2_CELL_SIZE
* + dec->len > 0
*/
static status_t cm_dec2_right_shift(const dec2_t *dec, int32 offset, dec2_t *rs)
{
int32 di = dec->len - 2;
int32 ri = di + offset;
ex 1.02 * 100^10 left shift to 1.02 * 100^30
len expn cells
3 0xcb 01 02
22 0xdf 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
to make sure ri < DEC2_CELL_SIZE - 1, must ignore the ri - (DEC2_CELL_SIZE - 1) digits
*/
if (ri >= (DEC2_CELL_SIZE - 1)) {
di -= (ri - (DEC2_CELL_SIZE - 1));
ri = (DEC2_CELL_SIZE - 1);
}
rs->len = (uint8)(ri + 2);
if (SECUREC_UNLIKELY((int32)GET_100_EXPN(dec) + (int32)offset) > DEC2_EXPN_UPPER_HALF) {
OG_THROW_ERROR(ERR_NUM_OVERFLOW);
return OG_ERROR;
}
rs->head = CONVERT_EXPN2(GET_100_EXPN(dec) + offset, IS_DEC_NEG(dec));
while (di >= 0) {
rs->cells[ri] = dec->cells[di];
ri--;
di--;
}
while (ri >= 0) {
rs->cells[ri] = 0;
ri--;
}
return OG_SUCCESS;
}
static inline void cm_dec2_rebuild_cells(dec2_t *rs, uint8 cell0)
{
if (GET_CELLS_SIZE(rs) == DEC2_CELL_SIZE) {
rs->len--;
}
errno_t err = memmove_s(rs->cells + sizeof(uint8), (DEC2_CELL_SIZE - 1) * sizeof(uint8), rs->cells,
GET_CELLS_SIZE(rs) * sizeof(uint8));
MEMS_RETVOID_IFERR(err);
rs->cells[0] = cell0;
rs->len++;
return;
}
static status_t cm_dec2_rebuild(dec2_t *rs, uint8 cell0)
{
int32 rs_expn = GET_100_EXPN(rs);
rs_expn += 1;
if (rs_expn > DEC2_EXPN_UPPER_HALF) {
OG_THROW_ERROR(ERR_NUM_OVERFLOW);
return OG_ERROR;
} else if (rs_expn < DEC2_EXPN_LOW_HALF) {
cm_zero_dec2(rs);
return OG_SUCCESS;
}
rs->head = CONVERT_EXPN2(rs_expn, IS_DEC_NEG(rs));
cm_dec2_rebuild_cells(rs, cell0);
return OG_SUCCESS;
}
* Quickly find the precision of a cells
* @note (1) The cell u0 should be specially treated;
* (2) The tailing zeros will not be counted. If all cell except u0 are
* zeros, then the precision of u0 is re-counted by ignoring tailing zeros
* e.g. | u0 = 1000 | u1 = 0 | u2 = 0 |..., the precision 1 will be
* returned.
*/
static int32 cm_dec2_calc_prec(const dec2_t *dec)
{
int32 i;
int32 prec = 0;
if (GET_CELLS_SIZE(dec) == 0) {
return 0;
}
for (i = GET_CELLS_SIZE(dec) - 1; i > 0; --i) {
if (dec->cells[i] > 0) {
prec = (i * DEC2_CELL_DIGIT - GET_ZEROS_TAIL_CELL(dec->cells[i]));
break;
}
}
if (i == 0) {
if (dec->cells[0] < 10) {
prec = 1;
} else {
prec = DEC2_CELL_DIGIT - GET_ZEROS_TAIL_CELL(dec->cells[0]);
}
} else {
prec += (int32)GET_DIGITS_HEAD_CELL(dec->cells[0]);
}
return prec;
}
* Truncate the tail of a decimal so that its precision is no more than prec
* It must be that prec > 0
*/
status_t cm_dec2_finalise(dec2_t *dec, uint32 prec)
{
uint32 dpos;
uint32 cpos;
uint32 npos;
uint32 carry;
int32 i;
int32 sci_exp = DEC2_GET_SEXP(dec);
if (DECIMAL2_IS_ZERO(dec) || sci_exp < DEC2_EXPN_LOW) {
cm_zero_dec2(dec);
return OG_SUCCESS;
}
DEC2_OVERFLOW_CHECK_BY_SCIEXP(sci_exp);
OG_RETSUC_IFTRUE(GET_CELLS_SIZE(dec) <= (prec / DEC2_CELL_DIGIT));
OG_RETVALUE_IFTRUE(((uint32)cm_dec2_calc_prec(dec) <= prec), OG_SUCCESS);
dpos = (uint32)DEC2_POS_N_BY_PREC0(prec, GET_DIGITS_HEAD_CELL(dec->cells[0]));
cpos = dpos / (uint32)DEC2_CELL_DIGIT;
npos = dpos % (uint32)DEC2_CELL_DIGIT;
carry = g_5ten_powers[DEC2_CELL_DIGIT - npos];
dec->len = cpos + 2;
for (i = (int32)cpos; i >= 0; --i) {
dec->cells[i] += carry;
carry = (dec->cells[i] >= DEC2_CELL_MASK);
if (carry == 0) {
break;
}
dec->cells[i] -= DEC2_CELL_MASK;
}
dec->cells[cpos] /= g_1ten_powers[DEC2_CELL_DIGIT - npos];
dec->cells[cpos] *= g_1ten_powers[DEC2_CELL_DIGIT - npos];
if (carry > 0) {
OG_RETURN_IFERR(cm_dec2_rebuild(dec, 1));
}
cm_dec2_trim_zeros(dec);
return OG_SUCCESS;
}
* Product a cell array with the digit at pos (starting from left) is k
*/
static status_t cm_dec2_make_round(const dec2_t *dec, uint32 pos, dec2_t *dx, bool8 *is_carry)
{
int32 i;
uint32 carry;
uint32 j;
*is_carry = OG_FALSE;
cm_dec2_copy(dx, dec);
if (pos >= DEC2_MAX_ALLOWED_PREC) {
return OG_SUCCESS;
}
i = (int32)(pos / DEC2_CELL_DIGIT);
j = pos % DEC2_CELL_DIGIT;
carry = (uint32)g_5ten_powers[DEC2_CELL_DIGIT - j];
for (; i >= 0; i--) {
dx->cells[i] += carry;
carry = (dx->cells[i] >= DEC2_CELL_MASK);
if (!carry) {
return OG_SUCCESS;
}
dx->cells[i] -= DEC2_CELL_MASK;
}
if (carry > 0) {
OG_RETURN_IFERR(cm_dec2_rebuild(dx, 1));
*is_carry = OG_TRUE;
}
return OG_SUCCESS;
}
static inline void cm_dec2_abs(dec2_t *decl)
{
if (!IS_DEC_NEG(decl)) {
return;
}
int expn = GET_10_EXPN(decl);
decl->head = CONVERT_EXPN(expn, OG_FALSE);
}
static inline bool32 cm_dec2_is_absolute_one(const dec2_t *dec)
{
return (bool32)(dec->len == 2 && dec->cells[0] == 1 &&
(dec->head == CONVERT_EXPN(0, OG_FALSE) || dec->head == CONVERT_EXPN(0, OG_TRUE)));
}
static inline bool32 cm_dec2_is_one(const dec2_t *dec)
{
return (bool32)(dec->len == 2 && dec->cells[0] == 1 && (dec->head == CONVERT_EXPN(0, OG_FALSE)));
}
static status_t cm_add_aligned_dec2(const dec2_t *d1, const dec2_t *d2, dec2_t *rs)
{
uint32 i;
c2typ_t carry = 0;
if (d1->len > d2->len) {
SWAP(const dec2_t *, d1, d2);
}
i = GET_CELLS_SIZE(d2);
while (i > GET_CELLS_SIZE(d1)) {
i--;
rs->cells[i] = d2->cells[i];
}
rs->head = d2->head;
rs->len = d2->len;
while (i-- > 0) {
rs->cells[i] = d1->cells[i] + d2->cells[i] + carry;
carry = (rs->cells[i] >= DEC2_CELL_MASK);
if (carry) {
rs->cells[i] -= DEC2_CELL_MASK;
}
}
if (carry) {
OG_RETURN_IFERR(cm_dec2_rebuild(rs, 1));
}
cm_dec2_trim_zeros(rs);
return OG_SUCCESS;
}
hit no borrow scenario
large small
1.04050607 1.03
01 04 05 06 07
01 03 99
hit borrow scenario
1.04 1.03050607
01 04
01 03 05 06 07
01 03 99 99 100
01 03 05 06 07
*/
static void cm_sub_aligned_dec2(const dec2_t *large, const dec2_t *small, bool32 flip_sign, dec2_t *rs)
{
int32 borrow = (small->len > large->len) ? 1 : 0;
uint32 i;
if ((bool32)borrow) {
i = GET_CELLS_SIZE(small) - 1;
rs->cells[i] = DEC2_CELL_MASK - small->cells[i];
while (i > (uint32)GET_CELLS_SIZE(large)) {
i--;
rs->cells[i] = (DEC2_CELL_MASK - 1) - small->cells[i];
}
rs->len = small->len;
} else {
i = GET_CELLS_SIZE(large);
while (i > GET_CELLS_SIZE(small)) {
i--;
rs->cells[i] = large->cells[i];
}
rs->len = large->len;
}
while (i-- > 0) {
int32 tmp = (int32)(large->cells[i] - (small->cells[i] + borrow));
borrow = (tmp < 0);
if (borrow) {
tmp += (int32)DEC2_CELL_MASK;
}
rs->cells[i] = (c2typ_t)tmp;
}
rs->head = flip_sign ? CONVERT_EXPN2(GET_100_EXPN(large), !IS_DEC_NEG(large)) : large->head;
if (SECUREC_UNLIKELY(rs->cells[0] == 0)) {
for (i = 1; i < GET_CELLS_SIZE(rs); i++) {
if (rs->cells[i] > 0) {
break;
}
}
cm_dec2_left_shift(rs, i, rs);
}
cm_dec2_trim_zeros(rs);
}
#define DEC2_CELL_FMT "%02u"
static inline void cm_trim_cell_text(text_t *text)
{
for (int i = (uint32)text->len - 1; i > 0; --i) {
if (!CM_IS_ZERO(text->str[i])) {
break;
}
--text->len;
}
}
* Convert the significant digits of cells into text with a maximal len
* @note The tailing zeros are removed when outputting
*/
static void cm_cell2s_to_text(const cell2_t cells, uint32 ncell, text_buf_t *text, int32 max_len)
{
uint32 i;
int iret_snprintf;
iret_snprintf = snprintf_s(text->str, text->max_size, DEC2_CELL_DIGIT, "%u", cells[0]);
PRTS_RETVOID_IFERR(iret_snprintf);
text->len = (uint32)iret_snprintf;
for (i = 1; (text->len < (uint32)max_len) && (i < ncell); ++i) {
iret_snprintf = snprintf_s(CM_GET_TAIL(text), text->max_size - text->len, DEC2_CELL_DIGIT, DEC2_CELL_FMT,
(uint32)cells[i]);
PRTS_RETVOID_IFERR(iret_snprintf);
text->len += (uint32)iret_snprintf;
}
if (text->len > (uint32)max_len) {
text->len = (uint32)max_len;
}
cm_trim_cell_text(&text->value);
}
* Round a decimal to a text with the maximal length max_len
* If the precision is greater than max_len, a rounding mode is used.
* The rounding mode may cause a change on precision, e.g., the 8-precision
* decimal 99999.999 rounds to 7-precision decimal is 100000.00, and then
* its actual precision is 8. The function will return the change. If
* no change occurs, zero is returned.
* @note
* Performance sensitivity.CM_ASSERT should be guaranteed by caller, i.g. 1.max_len > 0 2.dec->cells[0] > 0
*/
static status_t cm_dec2_round_to_text(const dec2_t *dec, int32 max_len, text_buf_t *text_out, int32 *round)
{
dec2_t txtdec;
uint8 prec_u0;
int32 prec;
bool8 is_carry;
*round = 0;
prec = cm_dec2_calc_prec(dec);
if (prec <= max_len) {
cm_cell2s_to_text(dec->cells, GET_CELLS_SIZE(dec), text_out, prec);
return OG_SUCCESS;
}
prec_u0 = cm_count_u8digits(dec->cells[0]);
prec = DEC2_POS_N_BY_PREC0(max_len, prec_u0);
OG_RETURN_IFERR(cm_dec2_make_round(dec, (uint32)prec, &txtdec, &is_carry));
if (is_carry) {
cm_cell2s_to_text(txtdec.cells, GET_CELLS_SIZE(&txtdec), text_out, max_len);
*round = 1;
} else {
cm_cell2s_to_text(txtdec.cells, GET_CELLS_SIZE(&txtdec), text_out, max_len);
*round = (cm_count_u8digits(txtdec.cells[0]) > prec_u0) ? 1 : 0;
}
return OG_SUCCESS;
}
* Convert a cell text into a cell of big integer by specifying the
* length digits in u0 (i.e., len_u0), and return the number of non-zero cells
* Performance sensitivity.CM_ASSERT should be guaranteed by caller, i.g. cells[0] > 0
*/
static int32 cm_digitext_to_cell2s(dec2_t *dec, num_part_t *np)
{
uint32 i;
uint32 k;
text_t cell_text;
digitext_t *dtext = &np->digit_text;
cell_text.str = dtext->str;
cell_text.len = np->dot_offset;
dec->cells[0] = (c2typ_t)cm_celltext2uint32(&cell_text);
k = 1;
for (i = (uint32)np->dot_offset; k < DEC2_CELL_SIZE && i < dtext->len; k++) {
cell_text.str = dtext->str + i;
cell_text.len = (uint32)DEC2_EXPN_UNIT;
dec->cells[k] = (c2typ_t)cm_celltext2uint32(&cell_text);
i += DEC2_CELL_DIGIT;
}
while (dec->cells[k - 1] == 0) {
--k;
}
return (int32)k;
}
-----------------------
| \ | | |
| \expn| even | odd |
|dot \ | | |
| \ | | |
-----------------------
|even | even | odd |
-----------------------
|odd | odd | even |
-----------------------
left shift dot, the abs(value) is smaller, so expn should add
shift right dot, the abs(value) is bigger, so expn should subtract
*/
static inline void cm_adjust_expn(num_part_t *np)
{
int32 sci_expn = np->sci_expn;
bool32 is_odd = cm_is_odd(sci_expn);
np->dot_offset = 1;
if (is_odd) {
np->dot_offset = 2;
np->sci_expn -= 1;
if (np->digit_text.len == 1) {
CM_TEXT_APPEND(&np->digit_text, '0');
}
}
}
* Convert a digit text with a scientific exponent into a decimal
* The digit text may be changed when adjust the scale of decimal to be
* an integral multiple of DEC2_CELL_DIGIT, by appending zeros.
* @return the precision of u0
* @note
* Performance sensitivity.CM_ASSERT should be guaranteed by caller,
* i.g. dtext->len > 0 && dtext->len <= (uint32)DEC2_MAX_ALLOWED_PREC
*/
static void cm_digitext_to_dec2(dec2_t *dec, num_part_t *np)
{
int32 delta;
dec->len = 1;
cm_adjust_expn(np);
CM_ASSERT(np->digit_text.len >= (uint32)np->dot_offset);
delta = np->digit_text.len - np->dot_offset;
delta %= DEC2_EXPN_UNIT;
if (delta == 1) {
CM_TEXT_APPEND(&np->digit_text, '0');
}
CM_NULL_TERM(&np->digit_text);
dec->head = CONVERT_EXPN(np->sci_expn, np->is_neg);
dec->len += cm_digitext_to_cell2s(dec, np);
return;
}
* Output a decimal type in scientific format, e.g., 2.34566E-20
*/
static status_t cm_dec2_to_sci_text(text_t *text, const dec2_t *dec, int32 max_len)
{
int32 i;
char obuff[OG_NUMBER_BUFFER_SIZE];
text_buf_t cell_text;
CM_INIT_TEXTBUF(&cell_text, OG_NUMBER_BUFFER_SIZE, obuff);
char sci_buff[DEC_EXPN_BUFF_SZ];
int32 sci_exp;
int32 placer;
int iret_snprintf;
int32 round;
sci_exp = DEC2_GET_SEXP(dec);
placer = (int32)IS_DEC_NEG(dec) + 3;
placer += (int32)cm_count_u8digits((uint8)abs(sci_exp));
if (max_len <= placer) {
return OG_ERROR;
}
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, max_len - placer, &cell_text, &round));
if (round > 0) {
++sci_exp;
}
iret_snprintf = snprintf_s(sci_buff, DEC_EXPN_BUFF_SZ, DEC_EXPN_BUFF_SZ - 1, "E%+d", sci_exp);
PRTS_RETURN_IFERR(iret_snprintf);
placer = iret_snprintf;
text->len = 0;
if (IS_DEC_NEG(dec)) {
CM_TEXT_APPEND(text, '-');
}
CM_TEXT_APPEND(text, cell_text.str[0]);
CM_TEXT_APPEND(text, '.');
for (i = 1; (int32)text->len < max_len - placer; ++i) {
if (i < (int32)cell_text.len) {
CM_TEXT_APPEND(text, cell_text.str[i]);
} else {
CM_TEXT_APPEND(text, '0');
}
}
errno_t ret = memcpy_sp(CM_GET_TAIL(text), max_len - text->len, sci_buff, placer);
MEMS_RETURN_IFERR(ret);
text->len += placer;
return OG_SUCCESS;
}
static status_t cm_dec2_text_dot_inside(text_t *text, const dec2_t *dec, text_buf_t *cell_text, int32 max_len,
int32 *dot)
{
int32 round;
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, max_len - text->len - 1, cell_text, &round));
*dot += round;
int32 dot_pos = *dot;
if ((int32)cell_text->len <= dot_pos) {
cm_concat_text(text, max_len, &cell_text->value);
cm_text_appendc(text, dot_pos - (int32)cell_text->len, '0');
} else {
OG_RETURN_IFERR(cm_concat_ntext(text, &cell_text->value, dot_pos));
CM_TEXT_APPEND(text, '.');
cell_text->str += (uint32)dot_pos;
cell_text->len -= (uint32)dot_pos;
cm_concat_text(text, max_len, &cell_text->value);
}
return OG_SUCCESS;
}
* @note
* Performance sensitivity.CM_ASSERT should be guaranteed by caller, i.g. dot_pos <= max_len - dec->sign
*/
static status_t cm_dec2_to_plain_text(text_t *text, const dec2_t *dec, int32 max_len, int32 sci_exp, int32 prec)
{
int32 dot_pos;
char obuff[OG_NUMBER_BUFFER_SIZE];
text_buf_t cell_text;
CM_INIT_TEXTBUF(&cell_text, OG_NUMBER_BUFFER_SIZE, obuff);
int32 round;
text->len = 0;
if (IS_DEC_NEG(dec)) {
CM_TEXT_APPEND(text, '-');
}
dot_pos = sci_exp + 1;
if (prec <= dot_pos) {
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, max_len - text->len, &cell_text, &round));
cm_concat_text(text, max_len, &cell_text.value);
if (max_len - (int32)text->len < dot_pos - prec) {
return OG_ERROR;
}
cm_text_appendc(text, dot_pos - prec, '0');
return OG_SUCCESS;
}
if (dot_pos == max_len - text->len) {
* then the dot is not outputted. Suppose max_len = 10,
* (1). 1234567890.222 --> 1234567890 is outputted
* If round mode products carry, e.g. the rounded value of
* 9999999999.9 is 10000000000, whose length is 11 and greater than
* max_len, then the scientific format is used to print the decimal
*/
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, dot_pos, &cell_text, &round));
if (round > 0) {
CM_TEXT_CLEAR(text);
return cm_dec2_to_sci_text(text, dec, max_len);
}
cm_concat_text(text, max_len, &cell_text.value);
cm_text_appendc(text, max_len - (int32)text->len, '0');
} else if (dot_pos == max_len - text->len - 1) {
* then only max_len-1 is print but the dot is emitted. Assume
* max_len = 10, the following cases output:
* (1). 123456789.2345 ==> 123456789 (.2345 is abandoned)
* (2). 987654321.56 ==> 987654322 (.56 is rounded to 1)
* If a carry happens, e.g., 999999999.6 ==> 1000000000, max_len
* number of digits will be printed.
* */
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, dot_pos, &cell_text, &round));
cm_concat_text(text, max_len, &cell_text.value);
cm_text_appendc(text, max_len + round - ((int32)text->len + 1), '0');
} else if (dot_pos >= 0) {
OG_RETURN_IFERR(cm_dec2_text_dot_inside(text, dec, &cell_text, max_len, &dot_pos));
} else {
* Thus, the maxi_len should consider sign, dot, and the adding zeros */
OG_RETURN_IFERR(cm_dec2_round_to_text(dec, max_len - text->len - 1 + dot_pos, &cell_text, &round));
dot_pos += round;
CM_TEXT_APPEND(text, '.');
cm_text_appendc(text, -dot_pos, '0');
OG_RETURN_IFERR(cm_concat_ntext(text, &cell_text.value, max_len - (int32)text->len));
}
return OG_SUCCESS;
}
* Convert a decimal into a text with a given maximal precision
* cm_dec2_to_text is not guaranteed end of \0, if need string, should use cm_dec2_to_str
*/
status_t cm_dec2_to_text(const dec2_t *dec, int32 max_len, text_t *text)
{
int32 sci_exp;
int32 prec;
int32 maxlen = max_len;
CM_POINTER2(dec, text);
maxlen = MIN(maxlen, (int32)(OG_NUMBER_BUFFER_SIZE - 1));
if (DECIMAL2_IS_ZERO(dec)) {
text->str[0] = '0';
text->len = 1;
return OG_SUCCESS;
}
sci_exp = DEC2_GET_SEXP(dec);
prec = cm_dec2_calc_prec(dec);
if ((sci_exp < -6 && (-sci_exp + prec + (int32)IS_DEC_NEG(dec) > maxlen)) ||
(sci_exp > 0 && (sci_exp + 1 + (int32)IS_DEC_NEG(dec) > maxlen))) {
return cm_dec2_to_sci_text(text, dec, maxlen);
}
return cm_dec2_to_plain_text(text, dec, maxlen, sci_exp, prec);
}
* Convert a decimal into C-string, and return the ac
* max_len should be consided \0, max len should buffer size
*/
status_t cm_dec2_to_str(const dec2_t *dec, int max_len, char *str)
{
text_t text;
text.str = str;
text.len = 0;
OG_RETURN_IFERR(cm_dec2_to_text(dec, max_len - 1, &text));
str[text.len] = '\0';
return OG_SUCCESS;
}
status_t cm_str_to_dec2(const char *str, dec2_t *dec)
{
text_t text;
cm_str2text((char *)str, &text);
return cm_text_to_dec2(&text, dec);
}
static status_t cm_do_numpart_round2(const num_part_t *np, dec2_t *dec)
{
c2typ_t carry = g_1ten_powers[GET_DIGITS_HEAD_CELL(dec->cells[0]) % DEC2_CELL_DIGIT];
int32 i = GET_CELLS_SIZE(dec);
while (--i >= 0) {
dec->cells[i] += carry;
carry = dec->cells[i] >= DEC2_CELL_MASK;
if (!carry) {
return OG_SUCCESS;
}
dec->cells[i] = dec->cells[i] - DEC2_CELL_MASK;
}
if (carry > 0) {
OG_RETURN_IFERR(cm_dec2_rebuild(dec, 1));
}
return OG_SUCCESS;
}
static num_errno_t cm_numpart_to_dec2(num_part_t *np, dec2_t *dec)
{
if (NUMPART_IS_ZERO(np)) {
cm_zero_dec2(dec);
return NERR_SUCCESS;
}
if (np->sci_expn > DEC2_EXPN_UPPER) {
return NERR_OVERFLOW;
} else if (np->sci_expn < DEC2_EXPN_LOW) {
cm_zero_dec2(dec);
return NERR_SUCCESS;
}
dec->len = 0;
dec->sign = !np->is_neg;
cm_digitext_to_dec2(dec, np);
if (np->do_round) {
if (cm_do_numpart_round2(np, dec) != OG_SUCCESS) {
return NERR_OVERFLOW;
}
cm_dec2_trim_zeros(dec);
}
return NERR_SUCCESS;
}
* Translates a text_t representation of a decimal into a decimal
* @param
* -- precision: records the precision of the decimal text. The initial value
* is -1, indicating no significant digit found. When a leading zero
* is found, the precision is set to 0, it means the merely
* significant digit is zero. precision > 0 represents the
* number of significant digits in the decimal text.
*/
status_t cm_text_to_dec2(const text_t *dec_text, dec2_t *dec)
{
num_errno_t err_no;
num_part_t np;
np.excl_flag = NF_NONE;
err_no = cm_split_num_text(dec_text, &np);
if (err_no != NERR_SUCCESS) {
OG_THROW_ERROR(ERR_INVALID_NUMBER, cm_get_num_errinfo(err_no));
return OG_ERROR;
}
err_no = cm_numpart_to_dec2(&np, dec);
if (err_no != NERR_SUCCESS) {
OG_THROW_ERROR(ERR_INVALID_NUMBER, cm_get_num_errinfo(err_no));
return OG_ERROR;
}
return OG_SUCCESS;
}
static int8 cm_uint_to_dec2_expn(uint64 u64)
{
if (u64 >= DEC2_POW5_MASK) {
if (u64 >= DEC2_POW8_MASK) {
return (u64 >= DEC2_POW9_MASK) ? 9 : 8;
}
if (u64 >= DEC2_POW6_MASK) {
return (u64 >= DEC2_POW7_MASK) ? 7 : 6;
}
return 5;
}
if (u64 >= DEC2_POW3_MASK) {
return (u64 >= DEC2_POW4_MASK) ? 4 : 3;
}
if (u64 >= DEC2_CELL_MASK) {
return (u64 >= DEC2_POW2_MASK) ? 2 : 1;
}
return 0;
}
* Fill a non-zero uint32 into decimal
* @note u64 > 0
*/
#define FILL_CELL(v, dec, pow, idx) \
do { \
(dec)->len++; \
(dec)->cells[(idx)++] = (uint8)((v) / (pow)); \
(v) = (v) % (pow); \
} while (0)
* Fill a non-zero uint64(u64 > 0) into decimal
*/
static void cm_fill_uint64_to_dec2(uint64 u64, dec2_t *dec)
{
dec->len = 1;
uint8 idx = 0;
int8 expn = cm_uint_to_dec2_expn(u64);
dec->head = CONVERT_EXPN2(expn, OG_FALSE);
switch (expn) {
case 9:
FILL_CELL(u64, dec, DEC2_POW9_MASK, idx);
case 8:
FILL_CELL(u64, dec, DEC2_POW8_MASK, idx);
case 7:
FILL_CELL(u64, dec, DEC2_POW7_MASK, idx);
case 6:
FILL_CELL(u64, dec, DEC2_POW6_MASK, idx);
case 5:
FILL_CELL(u64, dec, DEC2_POW5_MASK, idx);
case 4:
FILL_CELL(u64, dec, DEC2_POW4_MASK, idx);
case 3:
FILL_CELL(u64, dec, DEC2_POW3_MASK, idx);
case 2:
FILL_CELL(u64, dec, DEC2_POW2_MASK, idx);
case 1:
FILL_CELL(u64, dec, DEC2_CELL_MASK, idx);
case 0:
dec->len++;
dec->cells[idx] = (c2typ_t)u64;
break;
default:
CM_NEVER;
break;
}
cm_dec2_trim_zeros(dec);
}
static inline void cm_dec2_negate(dec2_t *dec)
{
if (DECIMAL2_IS_ZERO(dec)) {
return;
} else {
int32 sci_exp = GET_100_EXPN(dec);
dec->head = CONVERT_EXPN2(sci_exp, !IS_DEC_NEG(dec));
}
}
* Convert an integer32 into a decimal
*/
void cm_int32_to_dec2(int32 i_32, dec2_t *dec)
{
int32 i32 = i_32;
bool32 is_neg = OG_FALSE;
if (i32 < 0) {
is_neg = OG_TRUE;
if (i32 == OG_MIN_INT32) {
cm_dec2_copy(dec, &DEC2_MIN_INT32);
return;
}
i32 = -i32;
} else if (i32 == 0) {
cm_zero_dec2(dec);
return;
}
cm_fill_uint64_to_dec2((uint64)i32, dec);
if (is_neg) {
cm_dec2_negate(dec);
}
}
void cm_uint32_to_dec2(uint32 i32, dec2_t *dec)
{
if (i32 == 0) {
cm_zero_dec2(dec);
return;
}
cm_fill_uint64_to_dec2((uint64)i32, dec);
}
** max_digits(int64) + DEC2_CELL_DIGIT + 1 than */
#define INT64_BUFF 32
* Convert an integer64 into a decimal
*/
void cm_int64_to_dec2(int64 i_64, dec2_t *dec)
{
int64 i64 = i_64;
bool32 is_neg = OG_FALSE;
if (i64 < 0) {
if (i64 == OG_MIN_INT64) {
cm_dec2_copy(dec, &DEC2_MIN_INT64);
return;
}
i64 = -i64;
is_neg = OG_TRUE;
} else if (i64 == 0) {
cm_zero_dec2(dec);
return;
}
cm_fill_uint64_to_dec2((uint64)i64, dec);
if (is_neg) {
cm_dec2_negate(dec);
}
}
static const double g_pos_pow2[] = {
1.0, 1.0e2, 1.0e4, 1.0e6, 1.0e8, 1.0e10, 1.0e12, 1.0e14, 1.0e16, 1.0e18,
1.0e20, 1.0e22, 1.0e24, 1.0e26, 1.0e28, 1.0e30, 1.0e32, 1.0e34, 1.0e36, 1.0e38,
1.0e40, 1.0e42, 1.0e44, 1.0e46, 1.0e48, 1.0e50, 1.0e52, 1.0e54, 1.0e56, 1.0e58,
1.0e60, 1.0e62, 1.0e64, 1.0e66, 1.0e68, 1.0e70, 1.0e72, 1.0e74, 1.0e76, 1.0e78,
1.0e80, 1.0e82, 1.0e84, 1.0e86, 1.0e88, 1.0e90, 1.0e92, 1.0e94, 1.0e96, 1.0e98,
1.0e100, 1.0e102, 1.0e104, 1.0e106, 1.0e108, 1.0e110, 1.0e112, 1.0e114, 1.0e116, 1.0e118,
1.0e120, 1.0e122, 1.0e124, 1.0e126, 1.0e128, 1.0e130,
};
* compute 10000^x, x should be between -40 and 40
*/
static inline double cm_pow2(int32 x)
{
int32 y = abs(x);
double r = (y < ARRAY_NUM(g_pos_pow2)) ? g_pos_pow2[y] : pow(10e2, y);
if (x < 0) {
r = 1.0 / r;
}
return r;
}
* Convert real value into a decimal. It is similar with the function cm_real_to_dec2.
* This function may be more efficient than cm_real_to_dec2, but may lose precision.
* It is suitable for an algorithm which needs an inexact initial value.
*/
static status_t cm_real_to_dec2_inexac(double real, dec2_t *dec)
{
double r = real;
if (SECUREC_UNLIKELY(!CM_DBL_IS_FINITE(r))) {
OG_THROW_ERROR(ERR_INVALID_NUMBER, "");
return OG_ERROR;
}
if (cm_compare_double(r, 0) == 0) {
cm_zero_dec2(dec);
return OG_SUCCESS;
}
uint8 index = 0;
double int_r;
int32 dexp;
bool32 is_neg = (r < 0);
if (is_neg) {
r = -r;
}
(void)frexp(r, &dexp);
dexp = (int32)((double)dexp * (double)OG_LOG10_2);
dexp &= 0xFFFFFFFE;
if (dexp < DEC2_EXPN_LOW) {
cm_zero_dec2(dec);
return OG_SUCCESS;
}
DEC2_OVERFLOW_CHECK_BY_SCIEXP(dexp);
dec->head = CONVERT_EXPN(dexp, is_neg);
r *= cm_pow2(-dexp / 2);
if (r >= 1.0) {
r = modf(r, &int_r);
dec->cells[index] = (c2typ_t)int_r;
index++;
} else {
dec->head = CONVERT_EXPN(dexp - 2, is_neg);
}
while (index < DEC2_TO_REAL_MAX_CELLS) {
if (cm_compare_double(r, 0) == 0) {
break;
}
r = modf(r * (double)DEC2_CELL_MASK, &int_r);
dec->cells[index++] = (c2typ_t)int_r;
}
dec->len = index + 1;
cm_dec2_trim_zeros(dec);
return OG_SUCCESS;
}
* Convert real value into a decimal type
*/
status_t cm_real_to_dec2(double real, dec2_t *dec)
{
OG_RETURN_IFERR(cm_real_to_dec2_inexac(real, dec));
return cm_dec2_finalise(dec, OG_MAX_REAL_PREC);
}
* NOTE THAT: convert a signed integer into DOUBLE is faster than unsigned integer,
* therefore, These codes use signed integer for conversation to DOUBLE as much as
* possible. The following SWITCH..CASE is faster than the loop implementation.
*/
double cm_dec2_to_real(const dec2_t *dec)
{
if (DECIMAL2_IS_ZERO(dec)) {
return 0.0;
}
double dval;
int32 pos = MIN(GET_CELLS_SIZE(dec), DEC2_TO_REAL_MAX_CELLS);
uint64 u64 = 0;
for (int i = pos; i > 0; i--) {
u64 += dec->cells[i - 1] * (uint64)g_pos_pow2[pos - i];
}
int32 dexpn = GET_100_EXPN(dec);
dexpn -= (pos - 1);
dval = (double)u64;
if (dexpn >= 0) {
dval *= g_pos_pow2[dexpn];
} else {
dval /= g_pos_pow2[-dexpn];
}
return IS_DEC_NEG(dec) ? -dval : dval;
}
* The core algorithm for adding of two decimals, without truncating
* the result.
* @see cm_decimal_add for adding of two decimals with truncation
*/
status_t cm_dec2_add_op(const dec2_t *d1, const dec2_t *d2, dec2_t *rs)
{
int32 offset;
dec2_t calc_dec;
bool32 is_same_sign;
if (DECIMAL2_IS_ZERO(d2)) {
goto DEC_ADD_ZERO;
}
if (DECIMAL2_IS_ZERO(d1)) {
d1 = d2;
goto DEC_ADD_ZERO;
}
offset = (int32)GET_100_EXPN(d1) - (int32)GET_100_EXPN(d2);
is_same_sign = (d1->sign == d2->sign);
if (offset != 0) {
if (offset < 0) {
offset = -offset;
SWAP(const dec2_t *, d1, d2);
}
if (offset >= DEC2_MAX_EXP_OFFSET) {
goto DEC_ADD_ZERO;
}
OG_RETURN_IFERR(cm_dec2_right_shift(d2, offset, &calc_dec));
d2 = &calc_dec;
} else if (!is_same_sign) {
int32 cmp = cm_dec2_cmp_cells(GET_PAYLOAD(d1), d1->len, GET_PAYLOAD(d2), d2->len);
if (cmp == 0) {
cm_zero_dec2(rs);
return OG_SUCCESS;
}
if (cmp < 0) {
SWAP(const dec2_t *, d1, d2);
}
}
if (is_same_sign) {
OG_RETURN_IFERR(cm_add_aligned_dec2(d1, d2, rs));
} else {
cm_sub_aligned_dec2(d1, d2, OG_FALSE, rs);
}
return OG_SUCCESS;
DEC_ADD_ZERO:
cm_dec2_copy(rs, d1);
return OG_SUCCESS;
}
* The core algorithm for subtracting of two decimals, without truncating
* the result.
* @see cm_decimal_sub for subtraction of two decimals with truncation
*/
status_t cm_dec2_sub_op(const dec2_t *d1, const dec2_t *d2, dec2_t *rs)
{
dec2_t calc_dec;
int32 offset;
bool32 do_swap = OG_FALSE;
bool32 is_same_sign;
if (DECIMAL2_IS_ZERO(d2)) {
goto DEC_SUB_ZERO;
}
if (DECIMAL2_IS_ZERO(d1)) {
do_swap = OG_TRUE;
d1 = d2;
goto DEC_SUB_ZERO;
}
offset = (int32)GET_100_EXPN(d1) - (int32)GET_100_EXPN(d2);
is_same_sign = (d1->sign == d2->sign);
if (offset != 0) {
if (offset < 0) {
offset = -offset;
SWAP(const dec2_t *, d1, d2);
do_swap = OG_TRUE;
}
if (offset >= DEC2_MAX_EXP_OFFSET) {
goto DEC_SUB_ZERO;
}
OG_RETURN_IFERR(cm_dec2_right_shift(d2, offset, &calc_dec));
d2 = &calc_dec;
} else if (is_same_sign) {
int32 cmp = cm_dec2_cmp_cells(GET_PAYLOAD(d1), d1->len, GET_PAYLOAD(d2), d2->len);
if (cmp == 0) {
cm_zero_dec2(rs);
return OG_SUCCESS;
}
if (cmp < 0) {
SWAP(const dec2_t *, d1, d2);
do_swap = OG_TRUE;
}
}
if (is_same_sign) {
cm_sub_aligned_dec2(d1, d2, do_swap, rs);
} else {
* the first operand. */
uint8 sign = do_swap ? d2->sign : d1->sign;
cm_add_aligned_dec2(d1, d2, rs);
rs->head = CONVERT_EXPN2(GET_100_EXPN(rs), !sign);
}
return OG_SUCCESS;
DEC_SUB_ZERO:
cm_dec2_copy(rs, d1);
if (do_swap && !DECIMAL2_IS_ZERO(rs)) {
cm_dec2_negate(rs);
}
return OG_SUCCESS;
}
* The core algorithm for multiplying of two decimals, without truncating
* the result.
* @see cm_dec2_mul_op for multiplying of two decimals with truncation
assume d1 has n1 cells, d2 has n2 cells, ingore carry, finally the result is (n1 + n2 - 1) cells.
for example d1 is 4 cells, d2 is 3 cells, precision is max 6 cells
a0 a1 a2 a3
b0 b1 b2
d1 * d2 = M^(e1+e2)* (a0 * M^0 + a1 * M^-1 + a2 * M^-2 + a3 * M^-3 ) * (b0 * M^0 + b1 * M^-1 + b2 * M^-2)
0 = (a0 * b0) * M +
1 (a0 * b1 + a1 * b0) * M^-1 +
2 (a0 * b2 + a1 * b1 + a2 * b0) * M^-2 +
3 (a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0) * M^-3 +
4 (a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0) * M^-4 +
5 (a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0) * M^-5
--------------------------------------------------------------------------------------------
(a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0) * M^-6 = 0
*/
status_t cm_dec2_mul_op(const dec2_t *d1, const dec2_t *d2, dec2_t *rs)
{
if (DECIMAL2_IS_ZERO(d1) || DECIMAL2_IS_ZERO(d2)) {
cm_zero_dec2(rs);
return OG_SUCCESS;
}
int32 i;
int32 j;
int32 n;
int32 carry = 0;
int32 ncells = GET_CELLS_SIZE(d1) + GET_CELLS_SIZE(d2) - 1;
i = GET_CELLS_SIZE(d2) - 1;
j = DEC2_CELL_SIZE - i;
for (; j < (int32)GET_CELLS_SIZE(d1); j++, i--) {
carry += (int32)d1->cells[j] * (int32)d2->cells[i];
}
carry /= DEC2_CELL_MASK;
i = MIN(ncells, DEC2_CELL_SIZE);
n = i;
while (i > 0) {
j = MIN(i, (int32)GET_CELLS_SIZE(d2)) - 1;
i--;
while (j >= 0 && (i - j) >= 0 && (i - j) < (int32)GET_CELLS_SIZE(d1)) {
carry += (c2typ_t)d1->cells[i - j] * (c2typ_t)d2->cells[j];
j--;
}
rs->cells[i] = carry % (c2typ_t)DEC2_CELL_MASK;
carry /= (c2typ_t)DEC2_CELL_MASK;
}
rs->len = (uint8)n + 1;
int32 rs_expn;
int32 carry_expn;
rs_expn = (int32)GET_100_EXPN(d1) + (int32)GET_100_EXPN(d2);
carry_expn = (carry > 0) ? 1 : 0;
rs_expn += carry_expn;
if (SECUREC_UNLIKELY(rs_expn > DEC2_EXPN_UPPER_HALF)) {
OG_THROW_ERROR(ERR_NUM_OVERFLOW);
return OG_ERROR;
} else if (SECUREC_UNLIKELY(rs_expn < DEC2_EXPN_LOW_HALF)) {
cm_zero_dec2(rs);
return OG_SUCCESS;
}
rs->head = CONVERT_EXPN2(rs_expn, d1->sign ^ d2->sign);
if (carry > 0) {
cm_dec2_rebuild_cells(rs, (uint8)carry);
}
cm_dec2_trim_zeros(rs);
return OG_SUCCESS;
}
* @note
* Performance sensitivity.CM_ASSERT should be guaranteed by caller, i.g. !DECIMAL2_IS_ZERO(d)
*/
static inline status_t cm_dec2_init_inverse(const dec2_t *d, dec2_t *d_inv)
{
return cm_real_to_dec2_inexac(1.0 / cm_dec2_to_real(d), d_inv);
}
* Computed the inverse of a decimal, inv_d = 1 / d
* The Newton Inversion algorithm is used:
* $x_{i+1} = 2x_{i} - dx^2_{i} = x_i(2-d * x_i)$
*/
static status_t cm_dec2_inv(const dec2_t *d, dec2_t *inv_d, uint32 prec)
{
uint32 i;
dec2_t delta;
OG_RETURN_IFERR(cm_dec2_init_inverse(d, inv_d));
DEC2_DEBUG_PRINT(inv_d, "inv_init_value");
for (i = 0; i <= 10; i++) {
OG_RETURN_IFERR(cm_dec2_mul_op(d, inv_d, &delta));
OG_RETURN_IFERR(cm_dec2_sub_op(&DEC2_ONE, &delta, &delta));
OG_RETURN_IFERR(cm_dec2_mul_op(&delta, inv_d, &delta));
DEC2_DEBUG_PRINT(&delta, "inv delta: %u", i);
OG_RETURN_IFERR(cm_dec2_add_op(inv_d, &delta, inv_d));
DEC2_DEBUG_PRINT(inv_d, "inv(x): %u", i);
if (cm_dec2_taylor_break(inv_d, &delta, prec)) {
break;
}
}
return OG_SUCCESS;
}
* The division of two decimals: dec1 / dec2
*/
status_t cm_dec2_divide(const dec2_t *dec1, const dec2_t *dec2, dec2_t *result)
{
dec2_t inv_y;
dec2_t x;
dec2_t y;
int32 n;
int32 m;
int32 sub;
uint8 res_sign;
if (SECUREC_UNLIKELY(DECIMAL2_IS_ZERO(dec1))) {
cm_zero_dec2(result);
return OG_SUCCESS;
}
if (SECUREC_UNLIKELY(DECIMAL2_IS_ZERO(dec2))) {
OG_THROW_ERROR(ERR_ZERO_DIVIDE);
return OG_ERROR;
}
if (cm_dec2_is_absolute_one(dec2)) {
cm_dec2_copy(result, dec1);
res_sign = dec1->sign ^ dec2->sign;
result->head = CONVERT_EXPN2(GET_100_EXPN(dec1), res_sign);
return OG_SUCCESS;
}
n = (int32)GET_100_EXPN(dec1);
m = (int32)GET_100_EXPN(dec2);
sub = n - m;
if (SECUREC_UNLIKELY((sub - 1) > DEC2_EXPN_UPPER_HALF) || SECUREC_UNLIKELY((sub) < DEC2_EXPN_LOW_HALF)) {
OG_THROW_ERROR(ERR_NUM_OVERFLOW);
return OG_ERROR;
}
cm_dec2_copy(&x, dec1);
cm_dec2_copy(&y, dec2);
x.head = CONVERT_EXPN2(0, IS_DEC_NEG(dec1));
y.head = CONVERT_EXPN2(0, IS_DEC_NEG(dec2));
OG_RETURN_IFERR(cm_dec2_inv(&y, &inv_y, MAX_NUM_CMP_PREC));
OG_RETURN_IFERR(cm_dec2_multiply(&x, &inv_y, result));
sub += (int32)GET_100_EXPN(result);
DEC2_OVERFLOW_CHECK_BY_EXPN(sub);
result->head = CONVERT_EXPN2(sub, IS_DEC_NEG(result));
return OG_SUCCESS;
}
* Get the carry of a decimal with negative expn when convert decimal into integer
* @note Required: expn < 0
*/
static int32 dec2_make_negexpn_round_value(dec2_t *dec, round_mode_t rnd_mode, int16 expn)
{
switch (rnd_mode) {
case ROUND_FLOOR:
return IS_DEC_NEG(dec) ? -1 : 0;
case ROUND_HALF_UP: {
int32 val = ((expn == -1) && (dec->cells[0] >= DEC2_HALF_MASK)) ? 1 : 0;
return IS_DEC_NEG(dec) ? -val : val;
}
case ROUND_CEILING:
return IS_DEC_NEG(dec) ? 0 : 1;
case ROUND_TRUNC:
default:
return 0;
}
}
static uint64 dec2_make_negexpn_round_value2(const dec2_t *dec, round_mode_t rnd_mode)
{
int8 expn;
switch (rnd_mode) {
case ROUND_HALF_UP:
expn = GET_100_EXPN(dec);
return ((expn == -1) && (dec->cells[0] >= DEC2_HALF_MASK)) ? 1 : 0;
case ROUND_CEILING:
return 1;
case ROUND_TRUNC:
case ROUND_FLOOR:
default:
return 0;
}
}
static status_t cm_make_dec2_to_int(dec2_t *dec, uint64 *u64, int8 expn, round_mode_t rnd_mode)
{
uint32 i = 1;
uint64 u64_val = dec->cells[0];
int32 inc;
for (; i <= (uint32)expn; i++) {
inc = (i >= GET_CELLS_SIZE(dec)) ? 0 : dec->cells[i];
u64_val = u64_val * DEC2_CELL_MASK + inc;
}
if (i < GET_CELLS_SIZE(dec)) {
switch (rnd_mode) {
case ROUND_CEILING:
u64_val += IS_DEC_NEG(dec) ? 0 : 1;
break;
case ROUND_FLOOR:
u64_val += IS_DEC_NEG(dec) ? 1 : 0;
break;
case ROUND_HALF_UP:
u64_val += (dec->cells[i] >= DEC2_HALF_MASK) ? 1 : 0;
break;
case ROUND_TRUNC:
default:
break;
}
}
*u64 = u64_val;
return OG_SUCCESS;
}
status_t cm_dec2_to_int64(dec2_t *dec, int64 *val, round_mode_t rnd_mode)
{
CM_POINTER(dec);
if (DECIMAL2_IS_ZERO(dec)) {
*val = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn < 0) {
*val = dec2_make_negexpn_round_value(dec, rnd_mode, expn);
return OG_SUCCESS;
}
if (expn > DEC2_MAX_INT64_POWER || (expn == DEC2_MAX_INT64_POWER && dec->cells[0] > 9)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "BIGINT");
return OG_ERROR;
}
uint64 u64;
OG_RETURN_IFERR(cm_make_dec2_to_int(dec, &u64, expn, rnd_mode));
return cm_dec2int64_check_overflow(u64, IS_DEC_NEG(dec), val);
}
static status_t cm_make_dec2_to_uint(const dec2_t *dec, uint64 *u64, int8 expn, round_mode_t rnd_mode)
{
uint32 i = 1;
uint64 u64_val = dec->cells[0];
uint32 inc;
for (; i <= (uint32)expn; i++) {
inc = (i >= GET_CELLS_SIZE(dec)) ? 0 : dec->cells[i];
u64_val = u64_val * DEC2_CELL_MASK + inc;
}
if (i < GET_CELLS_SIZE(dec)) {
switch (rnd_mode) {
case ROUND_CEILING:
u64_val += 1;
break;
case ROUND_HALF_UP:
u64_val += (dec->cells[i] >= DEC2_HALF_MASK) ? 1 : 0;
break;
case ROUND_FLOOR:
case ROUND_TRUNC:
default:
break;
}
}
*u64 = u64_val;
return OG_SUCCESS;
}
* Convert a decimal into uint32. if overflow happened, return ERROR
*/
status_t cm_dec2_to_uint32(dec2_t *dec, uint32 *i32, round_mode_t rnd_mode)
{
if (DECIMAL2_IS_ZERO(dec)) {
*i32 = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn > DEC2_MAX_INT32_POWER || IS_DEC_NEG(dec)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "UNSIGNED INTEGER");
return OG_ERROR;
}
if (expn < 0) {
*i32 = (uint32)dec2_make_negexpn_round_value(dec, rnd_mode, expn);
return OG_SUCCESS;
}
uint64 u64;
OG_RETURN_IFERR(cm_make_dec2_to_uint(dec, &u64, expn, rnd_mode));
TO_UINT32_OVERFLOW_CHECK(u64, uint64);
*i32 = (uint32)u64;
return OG_SUCCESS;
}
status_t cm_dec2_to_uint16(dec2_t *dec, uint16 *i16, round_mode_t rnd_mode)
{
if (DECIMAL2_IS_ZERO(dec)) {
*i16 = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn > DEC2_MAX_INT16_POWER || IS_DEC_NEG(dec)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "UNSIGNED INTEGER");
return OG_ERROR;
}
if (expn < 0) {
*i16 = (uint16)dec2_make_negexpn_round_value(dec, rnd_mode, expn);
return OG_SUCCESS;
}
uint64 u64;
OG_RETURN_IFERR(cm_make_dec2_to_uint(dec, &u64, expn, rnd_mode));
if (u64 < (uint64)OG_MIN_UINT16 || u64 > (uint64)OG_MAX_UINT16) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "UNSIGNED SHORT");
return OG_ERROR;
}
*i16 = (uint16)u64;
return OG_SUCCESS;
}
status_t cm_dec2_to_uint64(const dec2_t *dec, uint64 *u64, round_mode_t rnd_mode)
{
if (IS_DEC_NEG(dec)) {
OG_THROW_ERROR(ERR_VALUE_ERROR, "convert NUMBER into UINT64 failed");
return OG_ERROR;
}
if (DECIMAL2_IS_ZERO(dec)) {
*u64 = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn < 0) {
*u64 = dec2_make_negexpn_round_value2(dec, rnd_mode);
return OG_SUCCESS;
}
if (expn > DEC2_MAX_INT64_POWER || (expn == DEC2_MAX_INT64_POWER && dec->cells[0] > 18)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "UINT64");
return OG_ERROR;
}
uint32 i;
uint32 inc;
uint64 u64h = dec->cells[0];
uint64 u64l = 0;
u64h *= g_pow100_u64[(uint32)expn];
for (i = 1; i <= (uint32)expn; i++) {
inc = (i >= GET_CELLS_SIZE(dec)) ? 0 : dec->cells[i];
u64l = u64l * DEC2_CELL_MASK + inc;
}
if (i < GET_CELLS_SIZE(dec)) {
switch (rnd_mode) {
case ROUND_CEILING:
u64l += IS_DEC_NEG(dec) ? 0 : 1;
break;
case ROUND_FLOOR:
u64l += IS_DEC_NEG(dec) ? 1 : 0;
break;
case ROUND_HALF_UP:
u64l += (dec->cells[i] >= DEC2_HALF_MASK) ? 1 : 0;
break;
case ROUND_TRUNC:
default:
break;
}
}
if (u64h == 18000000000000000000uLL && u64l > 446744073709551615uLL) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "UINT64");
return OG_ERROR;
}
*u64 = u64h + u64l;
return OG_SUCCESS;
}
status_t cm_dec2_to_int32(dec2_t *dec, int32 *i32, round_mode_t rnd_mode)
{
if (DECIMAL2_IS_ZERO(dec)) {
*i32 = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn < 0) {
*i32 = dec2_make_negexpn_round_value(dec, rnd_mode, expn);
return OG_SUCCESS;
}
if (expn > DEC2_MAX_INT32_POWER || (expn == DEC2_MAX_INT32_POWER && dec->cells[0] > 21)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "INTEGER");
return OG_ERROR;
}
int64 i64;
OG_RETURN_IFERR(cm_make_dec2_to_int(dec, (uint64 *)&i64, expn, rnd_mode));
if (IS_DEC_NEG(dec)) {
i64 = -i64;
}
INT32_OVERFLOW_CHECK(i64);
*i32 = (int32)i64;
return OG_SUCCESS;
}
status_t cm_dec2_to_int16(dec2_t *dec, int16 *i16, round_mode_t rnd_mode)
{
if (DECIMAL2_IS_ZERO(dec)) {
*i16 = 0;
return OG_SUCCESS;
}
int8 expn = GET_100_EXPN(dec);
if (expn < 0) {
*i16 = (int16)dec2_make_negexpn_round_value(dec, rnd_mode, expn);
return OG_SUCCESS;
}
if (expn > DEC2_MAX_INT16_POWER || (expn == DEC2_MAX_INT16_POWER && dec->cells[0] > 3)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "SHORT");
return OG_ERROR;
}
int64 i64;
OG_RETURN_IFERR(cm_make_dec2_to_int(dec, (uint64 *)&i64, expn, rnd_mode));
if (IS_DEC_NEG(dec)) {
i64 = -i64;
}
if (i64 > OG_MAX_INT16 || i64 < OG_MIN_INT16) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "SHORT");
return OG_ERROR;
}
*i16 = (int16)i64;
return OG_SUCCESS;
}
bool32 cm_dec2_is_integer(const dec2_t *dec)
{
uint32 i;
if (DECIMAL2_IS_ZERO(dec)) {
return OG_TRUE;
}
int8 expn = GET_100_EXPN(dec);
if (expn < 0) {
return OG_FALSE;
}
i = expn + 1;
for (; i < GET_CELLS_SIZE(dec); i++) {
if (dec->cells[i] > 0) {
return OG_FALSE;
}
}
return OG_TRUE;
}
* The round mode can only be ROUND_HALF_UP or ROUND_TRUNC
* Performance sensitivity.CM_ASSERT should be guaranteed by caller,
* i.g. rnd_mode == ROUND_HALF_UP || rnd_mode == ROUND_TRUNC
*/
static status_t cm_dec2_scale(dec2_t *dec, int32 scale, round_mode_t rnd_mode)
{
int32 i;
int32 cpos;
int32 r_pos;
uint32 carry;
uint32 npos;
OG_RETVALUE_IFTRUE(DECIMAL2_IS_ZERO(dec), OG_SUCCESS);
int32 expn = GET_10_EXPN(dec);
r_pos = DEC2_CELL_DIGIT + expn + scale;
if (r_pos < 0) {
cm_zero_dec2(dec);
return OG_SUCCESS;
}
OG_RETVALUE_IFTRUE((r_pos > (int32)DEC2_MAX_ALLOWED_PREC), OG_SUCCESS);
cpos = r_pos / DEC2_CELL_DIGIT;
if (cpos >= (int32)GET_CELLS_SIZE(dec)) {
return OG_SUCCESS;
}
npos = DEC2_CELL_DIGIT - ((uint32)r_pos % DEC2_CELL_DIGIT);
carry = (rnd_mode == ROUND_HALF_UP) ? g_5ten_powers[npos] : 0;
for (i = cpos; i >= 0; --i) {
dec->cells[i] += carry;
carry = (dec->cells[i] >= DEC2_CELL_MASK);
if (!carry) {
break;
}
dec->cells[i] -= DEC2_CELL_MASK;
}
dec->cells[cpos] /= g_1ten_powers[npos];
dec->cells[cpos] *= g_1ten_powers[npos];
while ((cpos >= 0) && (dec->cells[cpos] == 0)) {
--cpos;
}
dec->len = (uint8)(cpos + 2);
if (carry) {
OG_RETURN_IFERR(cm_dec2_rebuild(dec, 1));
}
cm_dec2_trim_zeros(dec);
return OG_SUCCESS;
}
* Compute the sin(x) using Taylor series, where x in (0, pi/4)
* sin x = x-x^3/3!+x^5/5!- x^7/7! + ...= sum(((-1)^(n)*(x^(2n+1)))/(2n+1)!) n= 0,1,2,3
*/
static status_t cm_dec2_sin_frac(const dec2_t *x, dec2_t *sin_x)
{
dec2_t x_pow2;
dec2_t x_i;
dec2_t item;
OG_RETURN_IFERR(cm_dec2_mul_op(x, x, &x_pow2));
cm_dec2_copy(sin_x, x);
cm_dec2_copy(&x_i, x);
for (uint32 i = _I(3); i < ELEMENT_COUNT(g_inv_fact); i += 2) {
OG_RETURN_IFERR(cm_dec2_mul_op(&x_i, &x_pow2, &x_i));
OG_RETURN_IFERR(cm_dec2_mul_op(&x_i, &g_inv_fact[i], &item));
DEC2_DEBUG_PRINT(&item, "The item at [%u]", i >> 1);
if (i & 2) {
OG_RETURN_IFERR(cm_dec2_add_op(sin_x, &item, sin_x));
} else {
OG_RETURN_IFERR(cm_dec2_sub_op(sin_x, &item, sin_x));
}
DEC2_DEBUG_PRINT(sin_x, "The %u-th iteration", i >> 1);
if (cm_dec2_taylor_break(sin_x, &item, MAX_NUM_CMP_PREC)) {
break;
}
}
return OG_SUCCESS;
}
* Compute the cos(x) using Taylor series, where x in (0, pi/4)
* cos x = 1-x^2/2!+x^4/4!-x^6/6! = sum((-1)^n*(x^2n/(2*n)!)) n = 0,...NAN
*/
static status_t cm_dec2_cos_frac(const dec2_t *x, dec2_t *cos_x)
{
dec2_t x_pow2;
dec2_t x_i;
dec2_t item;
OG_RETURN_IFERR(cm_dec2_mul_op(x, x, &x_pow2));
cm_dec2_copy(&x_i, &x_pow2);
OG_RETURN_IFERR(cm_dec2_mul_op(&x_pow2, &DEC2_HALF_ONE, &item));
OG_RETURN_IFERR(cm_dec2_sub_op(&DEC2_ONE, &item, cos_x));
for (uint32 i = _I(4); i < ELEMENT_COUNT(g_inv_fact); i += 2) {
OG_RETURN_IFERR(cm_dec2_mul_op(&x_i, &x_pow2, &x_i));
OG_RETURN_IFERR(cm_dec2_mul_op(&x_i, &g_inv_fact[i], &item));
DEC2_DEBUG_PRINT(&item, "The item at [%u]", i >> 1);
if (i & 2) {
OG_RETURN_IFERR(cm_dec2_sub_op(cos_x, &item, cos_x));
} else {
OG_RETURN_IFERR(cm_dec2_add_op(cos_x, &item, cos_x));
}
DEC2_DEBUG_PRINT(cos_x, "The %u-th iteration", i >> 1);
if (cm_dec2_taylor_break(cos_x, &item, MAX_NUM_CMP_PREC)) {
break;
}
}
return OG_SUCCESS;
}
#define MAX2_RANGE_PREC (MAX_NUM_CMP_PREC - DEC2_CELL_DIGIT)
static status_t cm_dec2_range_to_2pi(const dec2_t *x, dec2_t *y, double *dy)
{
static const double pi = OG_PI * 2.0;
*y = *x;
dec2_t rem;
int32 scale;
do {
*dy = cm_dec2_to_real(y);
if (*dy < pi) {
break;
}
OG_RETURN_IFERR(cm_dec2_mul_op(&DEC2_INV_2PI, y, &rem));
int8 expn = GET_100_EXPN(&rem);
scale = (expn <= SEXP_2_D2EXP(MAX2_RANGE_PREC)) ? 0 : (MAX2_RANGE_PREC) - D2EXP_2_SEXP(expn);
OG_RETURN_IFERR(cm_dec2_scale(&rem, scale, ROUND_TRUNC));
OG_RETURN_IFERR(cm_dec2_mul_op(&rem, &DEC2_2PI, &rem));
OG_RETURN_IFERR(cm_dec2_sub_op(y, &rem, y));
} while (1);
return OG_SUCCESS;
}
static status_t cm_dec2_sin_op(const dec2_t *x, dec2_t *sin_x)
{
dec2_t tx;
double dx;
dec2_t tmp_x;
bool32 is_neg = IS_DEC_NEG(x);
cm_dec2_copy(&tmp_x, x);
cm_dec2_abs(&tmp_x);
OG_RETURN_IFERR(cm_dec2_range_to_2pi(&tmp_x, &tx, &dx));
if (dx < OG_PI_2) {
} else if (dx < OG_PI) {
OG_RETURN_IFERR(cm_dec2_sub_op(&DEC2_PI, &tx, &tx));
} else if (dx < OG_PI_2 + OG_PI) {
OG_RETURN_IFERR(cm_dec2_sub_op(&tx, &DEC2_PI, &tx));
is_neg = !is_neg;
} else {
OG_RETURN_IFERR(cm_dec2_sub_op(&DEC2_2PI, &tx, &tx));
is_neg = !is_neg;
}
dx = cm_dec2_to_real(&tx);
if (dx < OG_PI_4) {
OG_RETURN_IFERR(cm_dec2_sin_frac(&tx, sin_x));
} else {
OG_RETURN_IFERR(cm_dec2_sub_op(&DEC2_HALF_PI, &tx, &tx));
OG_RETURN_IFERR(cm_dec2_cos_frac(&tx, sin_x));
}
if (is_neg) {
cm_dec2_negate(sin_x);
}
return OG_SUCCESS;
}
* Compute the sin(x) using Taylor series
* sin x = x-x^3/3!+x^5/5!- x^7/7! + ...= sum(((-1)^(n)*(x^(2n+1)))/(2n+1)!) n= 0,1,2,3
*/
status_t cm_dec2_sin(const dec2_t *dec, dec2_t *result)
{
if (DECIMAL2_IS_ZERO(dec)) {
cm_zero_dec2(result);
return OG_SUCCESS;
}
OG_RETURN_IFERR(cm_dec2_sin_op(dec, result));
return cm_dec2_finalise(result, MAX_NUMERIC_BUFF);
}
* Convert a decimal into a text with all precisions
*/
static inline status_t cm_dec2_to_text_all(const dec2_t *dec, text_buf_t *text)
{
if (text->max_size <= OG_MAX_DEC_OUTPUT_ALL_PREC) {
return OG_ERROR;
}
return cm_dec2_to_text(dec, OG_MAX_DEC_OUTPUT_ALL_PREC, &text->value);
}
static inline status_t cm_dec2_to_str_all(const dec2_t *dec, char *str, uint32 buffer_len)
{
text_buf_t text_buf = { .str = str, .len = 0, .max_size = buffer_len };
OG_RETURN_IFERR(cm_dec2_to_text_all(dec, &text_buf));
str[text_buf.len] = '\0';
return OG_SUCCESS;
}
* Use for debugging. see the macro @DEC2_DEBUG_PRINT
*/
void cm_dec2_print(const dec2_t *dec, const char *file, uint32 line, const char *func_name, const char *fmt, ...)
{
char buf[100];
va_list var_list;
dec2_t fl_dec;
printf("%s:%u:%s\n", file, line, func_name);
va_start(var_list, fmt);
PRTS_RETVOID_IFERR(vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, fmt, var_list));
va_end(var_list);
printf("%s\n", buf);
(void)cm_dec2_to_str_all(dec, buf, sizeof(buf));
printf("dec := %s\n", buf);
printf(" ncells = %u, expn = %d, sign = %c, bytes = %d\n", GET_CELLS_SIZE(dec), GET_10_EXPN(dec),
(IS_DEC_NEG(dec)) ? '-' : '+', dec->len + 1);
printf(" cells = { ");
for (uint32 i = 0; i < GET_CELLS_SIZE(dec); i++) {
if (i != 0) {
printf(", ");
}
printf("%02u", dec->cells[i]);
}
printf("}\n");
fl_dec = *dec;
(void)cm_dec2_finalise(&fl_dec, MAX_NUM_CMP_PREC);
(void)cm_dec2_to_str_all(&fl_dec, buf, sizeof(buf));
printf("finalized dec := %s\n\n", buf);
(void)fflush(stdout);
}
#ifdef __cplusplus
}
#endif