/** * @name CeL function for numeral systems * @fileoverview 本檔案包含了記數系統用的 functions。 * * @since * * @see List of numeral systems */ 'use strict'; if (false) { CeL.run('data.numeral', function() { CeL.to_Chinese_numeral(1000); }); } // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'data.numeral', // data.native: .chars() require : 'data.code.compatibility.|data.native.', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { var /** {Number}未發現之index。 const: 基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1) */ NOT_FOUND = ''.indexOf('_'); // nothing required /** * null module constructor * * @class 處理記數系統的 functions */ var _// JSDT:_module_ = function() { // null module constructor }; /** * for JSDT: 有 prototype 才會將之當作 Class */ _// JSDT:_module_ .prototype = {}; // ----------------------------------------------------------------------------------------------------------------- // 中文數字 (Chinese numerals) function to_search_pattern(keys) { var key, chars = [], long_keys = []; function add(key) { if (key) if (key.length === 1) chars.push(key); else long_keys.push(key); } if (Array.isArray(keys)) keys.forEach(add); else for (key in keys) add(key); chars = chars.length > 0 ? '[' + chars.join('') + ']' : ''; if (long_keys.length > 0 && chars) long_keys.push(chars); // /(?:long_keys|long_keys|[chars])/g // /[chars]/g return new RegExp(long_keys.length > 0 ? '(?:' + long_keys.join('|') + ')' : chars, 'g'); } var // 小寫數字 Chinese_numerals_Normal_digits = '〇一二三四五六七八九', // Chinese_numerals_Normal_digits_Array // = Chinese_numerals_Normal_digits.split(''), // Chinese_numerals_Normal_digits_pattern // = to_search_pattern(Chinese_numerals_Normal_digits_Array), // numerals_Normal_pattern = new RegExp('(' // + Chinese_numerals_Normal_digits_pattern.source + '|\\d+)', 'g'), // 籌算: 步十百千萬 amount_pattern = new RegExp(numerals_Normal_pattern.source + '?([十百千])', 'g'), // 正式大寫數字 Chinese_numerals_Formal_digits = '零壹貳參肆伍陸柒捌玖', // Chinese_numerals_Formal_digits_Array // = Chinese_numerals_Formal_digits.split(''), // Chinese_numerals_Formal_digits_pattern // = to_search_pattern(Chinese_numerals_Formal_digits_Array), // http://thdl.ntu.edu.tw/suzhou/ // 蘇州碼子又稱花碼、番仔碼、草碼、菁仔碼 Suzhou_numerals_digits = '〇〡〢〣〤〥〦〧〨〩', // Counting Rod Numerals As of Unicode version 8.0 Counting_rod_numerals_digits // https://en.wikipedia.org/wiki/Counting_Rod_Numerals = '𝍠𝍡𝍢𝍣𝍤𝍥𝍦𝍧𝍨𝍩𝍪𝍫𝍬𝍭𝍮𝍯𝍰𝍱', // 全形阿拉伯數字 U+FF10~U+FF19 FULLWIDTH DIGIT FULLWIDTH_DIGITS = '0123456789', // positional_Chinese_numerals_digits // = Chinese_numerals_Normal_digits // + Chinese_numerals_Formal_digits // + Suzhou_numerals_digits.slice(1) + FULLWIDTH_DIGITS, // positional_Chinese_numerals_digits_pattern // = new RegExp('[' + positional_Chinese_numerals_digits + ']', 'g'), // only_positional_Chinese_numerals_digits_pattern // = new RegExp('^[' + positional_Chinese_numerals_digits + ']+$'), // 舊時/非正式/通用數字 正規化 numeral_convert_pair = { // o : '〇', O : '〇', '○' : '〇', 弌 : '壹', 弍 : '貳', 兩 : '二', 叁 : '參', 叄 : '參', 弎 : '參', 亖 : '四', // Firefox/3.0.19 無法 parse '䦉': 錯誤: invalid property id '䦉' : '肆', // [[ja:大字 (数字)]] 壱 : '壹', 弐 : '貳', 貮 : '貳', 参 : '參', 陆 : '陸', // 去除常用字以防 false positive // 漆 : '柒', // 俗亦以「什」代拾,然易竄為「仟」。 // 什 : '拾', // 念圓 : '貳拾圓', // 念 : '貳拾', 廿 : '二十', 卄 : '二十', 卅 : '三十', // http://www.bsm.org.cn/show_article.php?id=1888 // "丗五年" = "卅五年" 丗 : '三十', // e.g., 卌又三年 卌 : '四十', // 罕作「圩」 // 圩 : '五十', 皕 : '二百', // 古亦作「陌」。 陌 : '佰', // 古亦作「阡」。 阡 : '仟', 万 : '萬', 萬萬 : '億', // 太常使用。 // 經 : '京', 杼 : '秭', 壤 : '穰', 厘 : '釐' // 太常使用。 // 毛 : '毫' }, // numeral_convert_pattern, // denomination, 萬進系統單位 // http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E6%95%B0%E5%AD%97 // http://zh.wikipedia.org/wiki/%E5%8D%81%E8%BF%9B%E5%88%B6 // http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E6%95%B0%E5%AD%97 // http://lists.w3.org/Archives/Public/www-style/2003Apr/0063.html // http://forum.moztw.org/viewtopic.php?t=3043 // http://www.moroo.com/uzokusou/misc/suumei/suumei.html // http://espero.51.net/qishng/zhao.htm // http://www.nchu.edu.tw/~material/nano/newsbook1.htm // http://www.moroo.com/uzokusou/misc/suumei/suumei1.html // 十億(吉),兆(萬億),千兆(拍),百京(艾),十垓(澤),秭(堯),秭:禾予;溝(土旁);,無量大數→,無量,大數;[載]之後的[極]有的用[報] // 異體:阿僧[禾氏],For Korean:阿僧祗;秭:禾予,抒,杼,For Korean:枾 For // Korean:不可思議(不:U+4E0D→U+F967) // Espana應該是梵文所譯 // 因為根據「大方廣佛華嚴經卷第四十五卷」中在「無量」這個數位以後還有無邊、無等、不可數、不可稱、不可思、不可量、不可說、不可說不可說,Espana應該是指上面其中一個..因為如果你有心查查Espana其實應該是解作西班牙文的「西班牙」 Chinese_numerals_Denominations // ',萬,億,兆,京,垓,秭,穰,溝,澗,正,載,極,恒河沙,阿僧祇,那由他,不可思議,無量大數' = ',萬,億,兆,京,垓,秭,穰,溝,澗,正,載,極', // Chinese_numerals_Denominations_Array // = Chinese_numerals_Denominations.split(','), // Chinese_numerals_Denominations_pattern // = to_search_pattern(Chinese_numerals_Denominations_Array), // Chinese_numerals_token_pattern // = new RegExp('(.*?)(' // + Chinese_numerals_Denominations_pattern.source + ')', 'g'), // TODO: // http://zh.wikipedia.org/wiki/%E5%8D%81%E9%80%80%E4%BD%8D // 比漠微細的,是自天竺的佛經上的數字。而這些「佛經數字」已成為「古代用法」了。 // 小數單位(十退位):分,釐(厘),毫(毛),絲(秒),忽,微,纖,沙,塵(納),埃,渺,漠(皮),模糊,逡巡,須臾(飛),瞬息,彈指,剎那(阿),六德(德),虛,空,清,淨 // or:,虛,空,清,淨→,空虛,清淨(仄),阿賴耶,阿摩羅,涅槃寂靜(攸) // 六釐英金庚款公債條例: 年息定為?釐, 年利率?厘 Chinese_numerals_Decimal_denominations = '分釐毫絲忽微纖沙塵埃渺漠', // numerals_Decimal_token_pattern // = new RegExp(numerals_Normal_pattern.source // + '([' + Chinese_numerals_Decimal_denominations + '])', 'g'), // 下數系統單位 Chinese_numerals_Normal_base_denomination // 籌算: 步十百千萬 = (',十,百,千' + Chinese_numerals_Denominations).split(','), // Chinese_numerals_Formal_base_denomination // = (',拾,佰,仟' + Chinese_numerals_Denominations).split(','), // Chinese_numerals_Normal_pattern = new RegExp('(?:負?(?:[' + Chinese_numerals_Normal_digits + '\\d ][' + Chinese_numerals_Normal_base_denomination.join('') + ']*|[' + Chinese_numerals_Normal_base_denomination.join('') + ']+)+(又|分之)?)+', 'g'), // Chinese_numerals_Normal_Full_matched = new RegExp('^(?:負?[' + Chinese_numerals_Normal_digits + '\\d ' + Chinese_numerals_Normal_base_denomination.join('') + '又]+|分之)+$'), // numeral_value = Object.create(null); _.Chinese_numerals_Normal_digits = Chinese_numerals_Normal_digits; _.Chinese_numerals_Formal_digits = Chinese_numerals_Formal_digits; _.Chinese_numerals_Denominations // = Chinese_numerals_Denominations_Array.join(''); (function() { var base, scale = 0; Chinese_numerals_Normal_digits_Array // .forEach(function(digits) { numeral_value[digits] = scale; scale++; }); base = scale; '十,百,千'.split(',') // 籌算: 步十百千萬 .forEach(function(denomination) { numeral_value[denomination] = scale; scale *= base; }); base = scale; Chinese_numerals_Denominations_Array // .forEach(function(denomination) { if (denomination) { numeral_value[denomination] = scale; scale *= base; } }); scale = .1; Chinese_numerals_Decimal_denominations.split('') // .forEach(function(denomination) { if (denomination) { numeral_value[denomination] = scale; scale /= 10; } }); for (scale = 1; // scale < Suzhou_numerals_digits.length; scale++) { base = Suzhou_numerals_digits.charAt(scale); numeral_value[base] = scale; numeral_convert_pair[base] // = Chinese_numerals_Normal_digits[scale]; } for (scale = 0; // scale < FULLWIDTH_DIGITS.length; scale++) { base = FULLWIDTH_DIGITS.charAt(scale); numeral_value[base] = scale; numeral_convert_pair[base] // = Chinese_numerals_Normal_digits[scale]; } numeral_convert_pattern // = to_search_pattern(numeral_convert_pair); })(); // 對所有非正規之數字。 // TODO (bug): 十廿, 二廿 function normalize_Chinese_numeral(number_String) { return number_String // .replace(/\s+/g, '') // .replace(numeral_convert_pattern, function($0) { return numeral_convert_pair[$0]; }); } _.normalize_Chinese_numeral = normalize_Chinese_numeral; function Chinese_numerals_Formal_to_Normal(number_String) { return number_String.replace(Chinese_numerals_Formal_digits_pattern, function($0) { return Chinese_numerals_Normal_digits // .charAt(Chinese_numerals_Formal_digits.indexOf($0)); }) // .replace(/[拾佰仟]/g, function(denomination) { return '十百千'.charAt('拾佰仟'.indexOf(denomination)); }); } _.Chinese_numerals_Formal_to_Normal // = Chinese_numerals_Formal_to_Normal; function Chinese_numerals_Normal_to_Formal(number_String) { return number_String.replace(Chinese_numerals_Normal_digits_pattern, function($0) { return Chinese_numerals_Formal_digits // .charAt(Chinese_numerals_Normal_digits.indexOf($0)); }) // .replace(/[十百千]/g, function($0) { return '拾佰仟'.charAt('十百千'.indexOf($0)); }); } _.Chinese_numerals_Normal_to_Formal // = Chinese_numerals_Normal_to_Formal; /** * 將漢字中文數字轉換為半形阿拉伯數字表示法(小數系統 0-99999) * * @deprecated use from_Chinese_numeral. */ function deprecated_from_Chinese_numeral(number_String) { if (!number_String || !isNaN(number_String)) return number_String; number_String = Chinese_numerals_Formal_to_Normal( // normalize_Chinese_numeral('' + number_String)); var i = 0, l, m, // n = Chinese_numerals_Normal_digits_Array, // 籌算: 萬千百十步 d = '萬千百十'.split(''), r = 0, /** * @see 日語數字 */ p = ('' + number_String).replace(/\s/g, '') // .replace(/[O○]/g, '〇'); for (; i < n.length; i++) n[n[i]] = i; for (i = 0; i < d.length; i++) { if (p && NOT_FOUND !== // (m = d[i] ? p.indexOf(d[i]) : p.length)) if (!m && d[i] === '十') r += 1, p = p.slice(1); else if (isNaN(l = n[ // p.slice(0, m).replace(/^〇+/, '')])) return number_String; else r += l, p = p.slice(m + 1); if (d[i]) r *= 10; } return r; } // More examples: see /_test suite/test.js function from_positional_Chinese_numeral(number_String) { return isNaN(number_String = number_String.replace( positional_Chinese_numerals_digits_pattern, function(digit) { return numeral_value[digit]; })) ? number_String : +number_String; } function to_positional_Chinese_numeral(number_String, formal) { formal = formal ? Chinese_numerals_Formal_digits_Array // : Chinese_numerals_Normal_digits_Array; return ('' + number_String) // .replace(/\d/g, function(digit) { return formal[digit]; }); } _.positional_Chinese_numerals_digits // = positional_Chinese_numerals_digits; _.from_positional_Chinese_numeral // = from_positional_Chinese_numeral; _.to_positional_Chinese_numeral // = to_positional_Chinese_numeral; // 將漢字中文數字轉換為半形阿拉伯數字表示法。(正常情況下:小數系統 0-9999) function from_Chinese_numeral_token(amount) { if (!isNaN(amount)) return +amount; // reset amount_pattern.lastIndex = 0; var token_sum = 0, matched, lastIndex = 0; while (matched = amount_pattern.exec(amount)) { lastIndex = amount_pattern.lastIndex; // [ , digit, denomination ] // for "一千零十一" 等。 token_sum += (matched[1] // && matched[1] !== '〇' ? numeral_value[matched[1]] : 1) // * numeral_value[matched[2]]; } // lastIndex 後面的全部放棄。 amount = amount.slice(lastIndex).replace(/^〇+/, ''); numerals_Normal_pattern.lastIndex = 0; matched = numerals_Normal_pattern.exec(amount); if (matched) token_sum += isNaN(matched = matched[0]) // ? numeral_value[matched] : +matched; return token_sum || 0; } /** * 將漢字中文數字轉換為阿拉伯數字表示法。
* 注意:本函數不會檢查 number_String 之正規與否! */ function from_Chinese_numeral(number_String) { if (!number_String || !isNaN(number_String)) return number_String; number_String = Chinese_numerals_Formal_to_Normal( // normalize_Chinese_numeral('' + number_String)); // console.log(Chinese_numerals_Normal_pattern); // console.log(JSON.stringify(number_String)); if (!Chinese_numerals_Normal_Full_matched.test(number_String)) { // 部分符合,僅針對符合部分處理。 Chinese_numerals_Normal_pattern.lastIndex = 0; return number_String.replace( // Chinese_numerals_Normal_pattern, function($0) { // console.log('-- ' + JSON.stringify($0)); // 避免前後空格被吃掉。 var token = $0.match(/^(\s*)(\S.*?)(\s*)$/); if (!token) { // 可能會是" " return $0; } var digit = token[2].charAt(0); token[2] = ('負十'.includes(digit) || positional_Chinese_numerals_digits.includes(digit) || (digit = token[2].charAt(1)) && ('十'.includes(digit) // || positional_Chinese_numerals_digits.includes(digit)) // 不處理過大的位值,例如 "正"。 ? from_Chinese_numeral(token[2]) : token[2]); return token[1] + token[2] + token[3]; }); } var sum = 0, lastIndex = 0, // negative = number_String.charAt(0) === '負', // matched = number_String // .match(/^(負)?(?:(.+)又)?(.+)分之(.+)$/); if (matched) { sum = (matched[2] // && from_Chinese_numeral(matched[2]) || 0) + from_Chinese_numeral(matched[4]) / from_Chinese_numeral(matched[3]); return negative ? -sum : sum; } // reset Chinese_numerals_token_pattern.lastIndex = 0; // console.log([ number_String, Chinese_numerals_token_pattern ]); while (matched = Chinese_numerals_token_pattern // .exec(number_String)) { // [ , amount, denomination ] // console.log(matched); sum += from_Chinese_numeral_token(matched[1] || 1) * numeral_value[matched[2]]; lastIndex = Chinese_numerals_token_pattern.lastIndex; } number_String = number_String.slice(lastIndex); // reset numerals_Decimal_token_pattern.lastIndex = 0; // console.log([ sum, number_String, numerals_Decimal_token_pattern ]); if (lastIndex = numerals_Decimal_token_pattern // .exec(number_String)) { // 輸入 '捌佰3分' 之類。 // console.log(lastIndex); lastIndex = lastIndex.index; matched = [ , number_String.slice(0, lastIndex), number_String.slice(lastIndex) ]; } else { // 輸入 '捌佰3又3分' 之類。 matched = number_String.match(/(.*)[點又.](.*)/) || [ , number_String ]; } if (false) { console .trace([ sum, matched, Chinese_numerals_Normal_Full_matched ]); console.trace([ only_positional_Chinese_numerals_digits_pattern .test(matched[1]) ]); } sum += only_positional_Chinese_numerals_digits_pattern.test(matched[1]) // e.g., CeL.from_Chinese_numeral('第一二三四章') ? from_positional_Chinese_numeral(matched[1]) : from_Chinese_numeral_token(matched[1]); // console.trace(sum); if (number_String = matched[2]) { // 處理小數。 for (var base = .1, lastIndex = 0;; base /= 10) { numerals_Decimal_token_pattern.lastIndex = lastIndex; if (matched = numerals_Decimal_token_pattern // .exec(number_String)) { lastIndex // = numerals_Decimal_token_pattern.lastIndex; // 單位 base = numeral_value[matched[2]]; matched = matched[1]; } else { numerals_Normal_pattern.lastIndex = lastIndex; if (matched = numerals_Normal_pattern // .exec(number_String)) { lastIndex // = numerals_Normal_pattern.lastIndex; matched = matched[0]; } else break; } if (isNaN(matched)) matched = numeral_value[matched]; else if (matched > 9) matched = matched.replace(/^(\d)/, '$1.'); else matched = +matched; sum += matched * base; } } return negative ? -sum : sum; } /** * 將阿拉伯數字轉為中文數字下數系統大寫(Long scale)、小寫(Short scale)兩種表示法/中文數字讀法
* 處理1-99999的數,尚有bug。 */ function to_Chinese_numeral_Low_scale(number_String, formal) { // 用r=[]約多花一倍時間! var i = 0, r = '', l = number_String.length - 1, d, // tnum = formal ? Chinese_numerals_Formal_digits_Array : Chinese_numerals_Normal_digits_Array, // zero = tnum[0], // tbd = formal ? Chinese_numerals_Formal_base_denomination : Chinese_numerals_Normal_base_denomination; for (; i <= l; i++) // if(d=parseInt(number_String.charAt(i)))比較慢 if ((d = number_String.charAt(i)) !== '0') // '〇一二三四五六七八'.charAt(d) 比較慢 r += tnum[d] + tbd[l - i]; else if (r.slice(-1) != zero) if (Math.floor(number_String.slice(i + 1))) r += zero; else break; return r; } if (false) (function() { // 2.016,2.297,2.016 var d = new Date, v = '12345236', i = 0, a; for (; i < 10000; i++) a = to_Chinese_numeral(v); alert(v + '\n→' + a + '\ntime:' + gDate(new Date - d)); }); /** * 將阿拉伯數字轉為萬進中文數字表示法。 num>1京時僅會取概數,此時得轉成string再輸入! TODO: 統整:尚有bug。 廿卅 小數 * * @param {Number}number * native number * @param {Boolean}[formal] * kind * * @returns {String} 中文數字 * */ function to_Chinese_numeral(number, formal) { // number = parseFloat(number); number = (typeof number === 'number' // ? number.toString(10) // : '' + number) // 避免前後空格被吃掉。 // .replace(/[,\s]/g, '') ; if (!/^[+\-]?(?:\d+(?:\.\d*)?|(?:\d*\.)?\d+)$/.test(number)) { // 非數值 return number.replace( // /[+\-]?(?:\d+(?:\.\d*)?|(?:\d*\.)?\d+)/g, function($0) { // 避免前後空格被吃掉。 var token = $0.match(/^(\s*)(\S.*?)(\s*)$/); if (!token) { // 可能會是" " return $0; } // console.log(token); return token[1] + to_Chinese_numeral(token[2], formal) + token[3]; }); } var j, // i:integer,整數; i, // d:decimal,小數 d = number.indexOf('.'), k, l, m, addZero = false, // tnum = formal ? Chinese_numerals_Formal_digits_Array // : Chinese_numerals_Normal_digits_Array, // zero = tnum[0]; if (d === NOT_FOUND) d = 0; else for (number = number.replace(/0+$/, ''), // i = number.slice(d + 1), // number = number.slice(0, d), // d = '', j = 0; j < i.length; j++) // 小數 d += tnum[i.charAt(j)]; // 至此 number 為整數。 if (number.charAt(0) === '-') i = '負', number = number.slice(1); else i = ''; number = number.replace(/^0+/, ''); m = number.length % 4, j = m - 4, l = (number.length - (m || 4)) / 4; // addZero=false, l=Math.floor((number.length-1)/4) for (; j < number.length; m = 0, l--) // 這邊得用 parseInt( ,10): // parseInt('0~')會用八進位,其他也有奇怪的效果。 if (Math.floor(m = m ? number.slice(0, m) : number .substr(j += 4, 4))) { m = to_Chinese_numeral_Low_scale(m, formal); if (addZero = addZero && m.charAt(0) != zero) { i += zero + m // + Chinese_numerals_Denominations_Array[l]; addZero = false; } else i += m // + Chinese_numerals_Denominations_Array[l]; } else addZero = true; // 習慣用法: 一十 → 十 return (i ? i.replace(/^(負)?[一壹]([十拾])/, '$1$2') : zero) + (d ? '點' + d : ''); } _.from_Chinese_numeral = from_Chinese_numeral; _.to_Chinese_numeral = to_Chinese_numeral; /** * 各區文化特色 - 貨幣轉換:
* 轉換成新臺幣中文大寫金額表示法。
* Converted into money notation. * * @example // More examples: see /_test suite/test.js * * * @param {Number|String}amount * 貨幣數量。 * @returns {String} 新臺幣金額中文大寫表示法。 * * @requires to_Chinese_numeral() */ function to_TWD(amount) { if (typeof amount === 'string') amount = amount.replace(/[\s,$]+/g, ''); amount = to_Chinese_numeral(amount, true) // 銀行習慣用法,零可以不用寫。 .replace(/([佰仟萬億兆京垓秭穰溝澗正載極])零/g, '$1') // 100000 → 壹拾萬圓整 .replace(/^拾/, '壹拾'); // 大寫金額數字應緊接“人民幣/港幣/台幣”字樣填寫,不得留有空位。 return '新臺幣' + (amount.includes('點') ? amount.replace( // /點(.)(.)?(.)?/, function($0, $1, $2, $3) { return '圓' + $1 + '角' // 日本明治時代臺灣 1圓=100錢=1000厘, 不使用"零"這個數字 // e.g., "五百三圓二十三錢五厘" + ($2 ? $2 + '分' + ($3 ? $3 + '文' : '') : ''); }) : // 在“元”(或“圓”)之後、應寫“整”(或“正”)字 // 在“角”之後,可以不寫“整”(或“正”)字 // 大寫金額數字有“分”的,“分”後面不寫“整”(或“正”)字。 amount + '圓整'); } _// JSDT:_module_ .to_TWD = to_TWD; /** * Japanese numerals * * @param {Number}number * native number * * @returns {String} Japanese numerals */ function to_Japanese_numeral(number) { return to_Chinese_numeral(number).replace(/〇/g, '').replace(/萬/, '万'); } _.to_Japanese_numeral = to_Japanese_numeral; // https://en.wikipedia.org/wiki/Long_and_short_scales // http://blog.functionalfun.net/2008/08/project-euler-problem-17-converting.html var English_numerals = { 0 : "zero", 1 : "one", 2 : "two", 3 : "three", 4 : "four", 5 : "five", 6 : "six", 7 : "seven", 8 : "eight", 9 : "nine", 10 : "ten", 11 : "eleven", 12 : "twelve", 13 : "thirteen", 14 : "fourteen", 15 : "fifteen", 16 : "sixteen", 17 : "seventeen", 18 : "eighteen", 19 : "nineteen", 20 : "twenty", 30 : "thirty", 40 : "forty", 50 : "fifty", 60 : "sixty", 70 : "seventy", 80 : "eighty", 90 : "ninety", 100 : "hundred", 1000 : "thousand", 1000000 : "million", 1000000000 : "billion", 1000000000000 : "trillion", 1000000000000000 : "quadrillion", // Number.isSafeInteger(1000000000000000000) === false '1000000000000000000' : "quintillion" }; // @inner function to_English_numeral_small(number) { // assert: number = 1 ~ 999 // hundreds var conversion = number / 100 | 0; if (number %= 100) if (number in English_numerals) number = English_numerals[number]; else { // units var _1 = number % 10; _1 = _1 ? English_numerals[_1] : ''; // tens number = number / 10 | 0; if (number) { number = English_numerals[number * 10]; if (_1) number += '-' + _1; } else number = _1; } if (conversion) { conversion = English_numerals[conversion] + ' ' + English_numerals[100]; if (number) conversion += ' and ' + number; } else conversion = number; return conversion; } // written out numbers in words. Get number name. // British usage // @see http://www.grammarbook.com/numbers/numbers.asp function to_English_numeral(number) { if (number != Math.floor(number)) { library_namespace.error('Cannot conver [' + number + ']!'); } number = Math.floor(number); if (number < 0) return "negative " + to_English_numeral(-number); if (number < 91 && (number in English_numerals)) // for zero. return English_numerals[number]; var base = 1000, unit = 1, conversion = [], remainder, // remainder, 0 ~ 999 (1000-1) small = number % base; while (number = Math.floor(number / base)) { unit *= base; if (remainder = number % base) conversion.unshift(to_English_numeral_small(remainder) + ' ' + English_numerals[unit]); } if (conversion = conversion.join(', ')) { if (small) conversion += ' and ' // + to_English_numeral_small(small); } else conversion = small ? to_English_numeral_small(small) : ''; return conversion; } _.to_English_numeral = to_English_numeral; // ----------------------------------------------------------------------------------------------------------------- // (十進位)位值直接轉換用 // https://en.wikipedia.org/wiki/Positional_notation function convert_positional(digit_set, name) { var digits; if (typeof digit_set !== 'string' || 10 !== // (digits = digit_set.chars()).length) { library_namespace.error('Invalid digits of [' + name + ']: (' + digits.length + ') [' + digit_set + ']'); return; } var PATTERN_numeral = new RegExp( digit_set.length === digits.length ? '[' + digit_set + ']' : digits.join('|'), 'g'); digits.forEach(function(digit, index) { numeral_convert_pair[digit] = index; }); /** * native number → positional numeral system * * @param {Number}number * native number * * @returns {String} specified numerals */ function to_numeral(number) { return String(number).replace(/\d/g, function(digit) { return digits[digit]; }); } /** * positional numeral system → native number * * @param {String}number * specified numerals * * @returns {Number} native number */ to_numeral.from = function from_numeral(number) { number = String(number).replace(PATTERN_numeral, function(digit) { return numeral_convert_pair[digit]; }); if (!isNaN(number)) number = Number(number); return number; } return to_numeral; } // http://wikimediafoundation.org/wiki/Template:ConvertDigit // https://github.com/esetera/Objavi/blob/master/digits.txt // https://de.wikipedia.org/wiki/Zahlzeichen_in_Unicode // TODO: https://en.wiktionary.org/wiki/8 (function() { var positional_digits = { // Eastern Arabic numerals // https://en.wikipedia.org/wiki/Eastern_Arabic_numerals // 中東阿拉伯文數字, 標準阿拉伯文數字 // Western Arabic / Hindu–Arabic numeral system: 0123456789 // 在埃及,「二」通常用另一種寫法。 Arabic : '٠١٢٣٤٥٦٧٨٩', // Perso-Arabic variant, Persian, Urdu, 東阿拉伯文數字 Perso : '۰۱۲۳۴۵۶۷۸۹', Balinese : '᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙', // Bengali numerals (সংখ্যা shôngkhæ), 孟加拉文數字, // Bengali-Assamese numerals // https://en.wikipedia.org/wiki/Bengali_numerals // ৴৵৶৷৸৹ Bangla : '০১২৩৪৫৬৭৮৯', Brahmi : '𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯', Chakma : '𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿', Cham : '꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙', // 天城文(देवनागरी / devanāgarī) // https://hi.wikipedia.org/wiki/%E0%A4%AE%E0%A5%80%E0%A4%A1%E0%A4%BF%E0%A4%AF%E0%A4%BE%E0%A4%B5%E0%A4%BF%E0%A4%95%E0%A4%BF:Gadget-Numeral_converter.js // https://hi.wikipedia.org/wiki/%E0%A4%B5%E0%A4%BF%E0%A4%95%E0%A4%BF%E0%A4%AA%E0%A5%80%E0%A4%A1%E0%A4%BF%E0%A4%AF%E0%A4%BE:%E0%A4%85%E0%A4%82%E0%A4%95_%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%B5%E0%A4%B0%E0%A5%8D%E0%A4%A4%E0%A4%95 Devanagari : '०१२३४५६७८९', Gujarati : '૦૧૨૩૪૫૬૭૮૯', // Gurmukhī numerals // https://en.wikipedia.org/wiki/Gurmukh%C4%AB_alphabet#Numerals Gurmukhi : '੦੧੨੩੪੫੬੭੮੯', Javanese : '꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙', Kannada : '೦೧೨೩೪೫೬೭೮೯', // Kayah Li Kayah_Li : '꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉', // Khmer, Cambodian, 高棉文數字. // https://km.wikipedia.org/wiki/%E1%9E%91%E1%9F%86%E1%9E%96%E1%9F%90%E1%9E%9A%E1%9E%82%E1%9F%86%E1%9E%9A%E1%9E%BC:Number_table_sorting Khmer : '០១២៣៤៥៦៧៨៩', // Tai Tham Hora 十進位數字系統。 Lanna : '᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉', // Tai Tham Tham 十進位數字系統。老傣文,又稱老傣仂文、蘭納文. Lanna script Tai_Tham : '᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙', // 寮國/寮文數字 Lao : '໐໑໒໓໔໕໖໗໘໙', Lepcha : '᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉', Limbu : '᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏', Malayalam : '൦൧൨൩൪൫൬൭൮൯', // Meitei-Mayek Meitei_Mayek : '꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹', Mongolian : '᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙', // or Burmese. 緬甸文數字. // 警告:其中非空! Myanmar : '၀၁၂၃၄၅၆၇၈၉', // 緬甸撣邦文十進位數字系統。 // 警告:其中非空! Myanmar_Shan : '႐႑႒႓႔႕႖႗႘႙', // Neu-Tai-Lue. Neu_Tai_Lue : '᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙', // N'Ko, r to l NKo : '߀߁߂߃߄߅߆߇߈߉', Oriya : '୦୧୨୩୪୫୬୭୮୯', // Ol Chiki decimal numeral system. 桑塔爾文十進位數字系統。 Ol_Chiki : '᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙', Osmanya : '𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩', Saurashtra : '꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙', Sharada : '𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙', // Sorang-Sompeng Sorang_Sompeng : '𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹', Sundanese : '᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹', Takri : '𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉', // Tamil (Grantha), 泰米爾文數字 // https://www.adobe.com/type/browser/pdfs/1965.pdf Tamil : '௦௧௨௩௪௫௬௭௮௯', Telugu : '౦౧౨౩౪౫౬౭౮౯', // 藏文數字 Tibetan : '༠༡༢༣༤༥༦༧༨༩', // 泰文數字 th:ตัวเลขไทย // https://th.wikipedia.org/wiki/%E0%B8%95%E0%B8%B1%E0%B8%A7%E0%B9%80%E0%B8%A5%E0%B8%82%E0%B9%84%E0%B8%97%E0%B8%A2 Thai : '๐๑๒๓๔๕๖๗๘๙', Vai : '꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩' }; for ( var name in positional_digits) { var to_numeral = convert_positional(positional_digits[name], name); if (to_numeral) { _['to_' + name + '_numeral'] = to_numeral; _['from_' + name + '_numeral'] = to_numeral.from; } } })(); // ----------------------------------------------------------------------------------------------------------------- // Roman numerals // https://en.wikipedia.org/wiki/Roman_numerals // https://en.wiktionary.org/wiki/Appendix:Roman_numerals // TODO: to Alternative forms var Roman_numeral_alternative = { 'ↅ' : 'VI', 'ↆ' : 'L', // Safari 11: Invalid character 'Ⅼ' : 'L', 'Ⅽ' : 'C', 'Ⅾ' : 'D', 'Ⅿ' : 'M', 'ⅼ' : 'L', 'ⅽ' : 'C', 'ⅾ' : 'D', 'ⅿ' : 'M', 'ↀ' : 'M' }, PATTERN_Roman_numeral_alternative, // Roman_numeral_pair = {}, // PATTERN_Roman = [], // assert: 2個一組為十進位。 Roman_numeral_value = 'IVXLCDMↁↂↇↈ'.split(''), // Roman_numeral_value[apostrophus_starts] 開始為 apostrophus 表示法。 apostrophus_starts = Roman_numeral_value.indexOf('ↁ'); Roman_numeral_value.forEach(function(digit, index) { var is_unit = index % 2 === 0, next; Roman_numeral_pair[digit] = (is_unit ? 1 : 5) * Math.pow(10, index / 2 | 0); if (is_unit) { var next = Roman_numeral_value[index + 1]; PATTERN_Roman.unshift('(' + (next ? digit + '[' + next + Roman_numeral_value[index + 2] + ']|' + next + '?' : '') + digit + '*)'); } }); // 千百十個: /(M*)(C[DM]|D?C*)(X[LC]|L?X*)(I[VX]|V?I*)/i PATTERN_Roman = new RegExp(PATTERN_Roman.join(''), 'i'); // console.log(PATTERN_Roman); // /(ↈ*)(ↂ[ↇↈ]|ↇ?ↂ*)(M[ↁↂ]|ↁ?M*)(C[DM]|D?C*)(X[LC]|L?X*)(I[VX]|V?I*)/i // apostrophus: expressed in "apostrophus" notation. function to_Roman_numeral(number, apostrophus) { if (!(number > 0) || number != (number | 0)) { /** * the word nulla (the Latin word meaning "none") was used by * medieval computists in lieu of 0.
* About 725, Bede or one of his colleagues used the letter N, the * initial of nulla, in a table of epacts, all written in Roman * numerals. */ // return number === 0 ? 'N' : number; return number; } /** {Natural}已處理的 Roman 數字。 */ var value = [], /** {Natural}剩下尚未處理的數值。 */ left = number | 0; // 將 apostrophus 轉成可接受的最大 index。 apostrophus = apostrophus ? Roman_numeral_value.length : apostrophus_starts; // index += 2: assert: 2個一組為十進位。 for (var index = 0; left > 0; index += 2) { if (index >= apostrophus) { library_namespace.error( // OUT OF RANGE: number ≥ 1000000 'The number is too large to be expressed in Roman numerals: ' + number); return; } var digits, /** {Integer}位值。 */ position = left % 10; left = left / 10 | 0; if ((position + 1) % 5 === 0 && apostrophus > // position = 4 or 9 時之特殊處置。必須有此數字表示法,才允許通過。 (digits = index + (position === 4 ? 1 : 2))) { digits = Roman_numeral_value[index] + Roman_numeral_value[digits]; } else { if (position > 4 // [index + 1] 可能已經越界。 && (digits = Roman_numeral_value[index + 1])) { position -= 5; } else { digits = ''; } digits += Roman_numeral_value[index].repeat(position); } value.push(digits); } return value.reverse().join(''); } function Roman_position(previous, position) { if (!position) return previous; if (position.length === 1) return previous + Roman_numeral_pair[position]; var _1 = Roman_numeral_pair[position[0]], // _2 = Roman_numeral_pair[position[1]]; if (_2 > _1) // assert: position.length === 2 return previous + _2 - _1; return previous + _1 + _2 * (position.length - 1); } // TODO: 'Ↄ', 'ↄ' function from_Roman_numeral(number) { var matched = normalize_Roman_numeral(number).match(PATTERN_Roman); return matched ? matched.slice(1).reduce(Roman_position, 0) : number; } function normalize_Roman_numeral(number) { return String(number) // 正規化。 .replace(PATTERN_Roman_numeral_alternative, function(digit) { return Roman_numeral_alternative[digit]; }); } _.to_Roman_numeral = to_Roman_numeral; _.from_Roman_numeral = from_Roman_numeral; _.normalize_Roman_numeral = normalize_Roman_numeral; 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ'.split('').forEach(function(digit, index) { Roman_numeral_alternative[digit] = to_Roman_numeral(index + 1); }); 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻ'.split('').forEach(function(digit, index) { Roman_numeral_alternative[digit] = to_Roman_numeral(index + 1); }); PATTERN_Roman_numeral_alternative = new RegExp('[' + Object.keys(Roman_numeral_alternative) + ']', 'g'); if (false) (function() { for (var i = 1; i < 50000; i++) if (i !== CeL.from_Roman_numeral(CeL.to_Roman_numeral(i))) throw 'Error: ' + i + ' → ' + CeL.to_Roman_numeral(i) + ' → ' + CeL.from_Roman_numeral(CeL.to_Roman_numeral(i)); }); // ----------------------------------------------------------------------------------------------------------------- return (_// JSDT:_module_ ); }