単精度、倍精度浮動小数点数の計算

by umhr
参考
http://ja.wikipedia.org/wiki/%E5%8D%98%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0
♥0 | Line 230 | Modified 2016-01-12 17:22:12 | MIT License
play

ActionScript3 source code

/**
 * Copyright umhr ( http://wonderfl.net/user/umhr )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/10Ye
 */

package  
{
    
    import com.bit101.components.InputText;
    import com.bit101.components.Label;
    import com.bit101.components.Style;
    import com.bit101.components.TextArea;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.ByteArray;
    /**
     * 単精度浮動小数点数
     * http://ja.wikipedia.org/wiki/%E5%8D%98%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0
     * ...
     * @author umhr
     */
    public class WonderflMain extends Sprite 
    {
        private var _textArea:TextArea;
        
        public function WonderflMain() 
        {
            init();
        }
        private function init():void 
        {
            if (stage) onInit();
            else addEventListener(Event.ADDED_TO_STAGE, onInit);
        }

        private function onInit(event:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, onInit);
            // entry point
            
            Style.embedFonts = false;
            Style.fontName = "_sans";
            Style.fontSize = 12;
            
            new Label(this, 8, 8, "Number:");
            new InputText(this, 65, 8, "0.1", onCalc).width = 200;
            
            _textArea = new TextArea(this, 8, 35);
            _textArea.width = 450;
            _textArea.height = 420;
            
            start(0.1);
        }
        
        private function onCalc(e:Event):void {
            var inputText:InputText = e.target as InputText;
            var num:Number = Number(inputText.text);
            
            _textArea.text = "";
            start(num);
        }
        
        private function start(num:Number):void {
            var byteArray:ByteArray = new ByteArray();
            var binary:String;
            
            _textArea.text += "入力値をNumber型に:" + num + "\n";
            _textArea.text += "===============================\n";
            _textArea.text += "倍精度(64bit)\n";
            _textArea.text += "===============================\n";
            _textArea.text += "ByteArrayを使用(模範解答)。\n";
            _textArea.text += "writeDouble()で書き込んだものを二進数表示\n";
            
            byteArray.writeDouble(num);
            byteArray.position = 0;
            binary = FloatingPoint.binaryFromByteArray(byteArray);
            separater(binary);
            byteArray.position = 0;
            _textArea.text += "readDouble()でNumberに戻す:" + byteArray.readDouble() + "\n";
            _textArea.text += "===============================\n";
            _textArea.text += "独自計算。\n";
            binary = FloatingPoint.binaryFromDecimal(num, true);
            separater(binary);
            _textArea.text += "二進数を倍精度でNumberに戻す:" + FloatingPoint.decimalFromBinary(binary) + "\n";
            
            byteArray.length = 0;
            
            _textArea.text += "===============================\n";
            _textArea.text += "単精度(32bit)\n";
            _textArea.text += "===============================\n";
            _textArea.text += "ByteArrayを使用(模範解答)。\n";
            _textArea.text += "writeFloat()で書き込んだものを二進数表示\n";
            
            byteArray.writeFloat(num);
            byteArray.position = 0;
            binary = FloatingPoint.binaryFromByteArray(byteArray);
            separater(binary);
            byteArray.position = 0;
            _textArea.text += "readFloat()でNumberに戻す:" + byteArray.readFloat() + "\n";
            _textArea.text += "===============================\n";
            _textArea.text += "独自計算。\n";
            binary = FloatingPoint.binaryFromDecimal(num, false);
            separater(binary);
            _textArea.text += "二進数を単精度でNumberに戻す:" + FloatingPoint.decimalFromBinary(binary) + "\n";
        }
        
        private function separater(binary:String):void {
            var isDouble:Boolean = binary.length == 64;
            
            var sign:String = binary.substr(0, 1);
            var exponent:String = isDouble?binary.substr(1, 11):binary.substr(1, 8);
            var fraction:String = isDouble?binary.substr(12):binary.substr(9);
            
            _textArea.text += "二進数 : " + binary + "\n";
            _textArea.text += "符号部 : " + sign + "\n";
            _textArea.text += "指数部 : " + exponent + "\n";
            _textArea.text += "仮数部 : " + fraction + "\n";
        }
        
    }
    
}


    import flash.utils.ByteArray;
    /**
     * ...
     * @author umhr
     */
     class FloatingPoint 
    {
        
        public function FloatingPoint() 
        {
            
        }
        /**
         * ByteArrayに書き込まれている二進数をStringとして返します。
         * @param    byteArray
         * @return
         */
        static public function binaryFromByteArray(byteArray:ByteArray):String {
            var binary:String = "";
            var n:int = byteArray.length;
            for (var i:int = 0; i < n; i++) 
            {
                binary += ("00000000" + byteArray.readUnsignedByte().toString(2)).substr( -8);
            }
            return binary;
        }
        /**
         * 二進数をNumber(10進数)にして返します。
         * 【参考】単精度浮動小数点数
         * http://ja.wikipedia.org/wiki/%E5%8D%98%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0
         * @param    binary
         * @return
         */
        static public function decimalFromBinary(binary:String):Number {
            var isDouble:Boolean = binary.length == 64;
            
            var sign:String = binary.substr(0, 1);
            var exponent:String = isDouble?binary.substr(1, 11):binary.substr(1, 8);
            var fraction:String = isDouble?binary.substr(12):binary.substr(9);
            
            var s:Number = calcSign(sign);
            
            var result:Number;
            if (parseInt(exponent) == 0) {
                // 指数部が0の場合
                if (parseInt(fraction) == 0) {
                    // 仮数部が0の場合
                    result = 0;
                }else {
                    // 仮数部が0で無い場合
                    // 非正規化数
                    result = s * Math.pow(2, 2 - Math.pow(2, exponent.length) * 0.5) * calcFraction(fraction, 0);
                }
            }else if (parseInt(exponent, 2) == Math.pow(2, exponent.length) - 1) {
                // 指数部が全て1の場合
                if (parseInt(fraction) == 0) {
                    // 仮数部が0の場合
                    result = s * Infinity;
                }else {
                    // 仮数部が0で無い場合
                    result = NaN;
                }
            }else {
                // 正規化数
                result = s * calcExponent(exponent) * calcFraction(fraction);
            }
            return result;
        }
        
        /**
         * 符号部
         * @param    sign
         * @return
         */
        static private function calcSign(sign:String):Number 
        {
            return Math.pow( -1, parseInt(sign));
        }
        /**
         * 指数部
         * @param    exponent
         * @return
         */
        static private function calcExponent(exponent:String):Number 
        {
            var result:Number = 1 - Math.pow(2, exponent.length) * 0.5;// -1023;
            var pow:Number = 1;
            var n:int = exponent.length;
            for (var i:int = 0; i < n; i++) 
            {
                result += (exponent.charAt(n - i - 1) == "1")?pow:0;
                pow *= 2;
            }
            return Math.pow(2, result);
        }
        /**
         * 仮数部
         * @param    fraction
         * @param    offset
         * @return
         */
        static private function calcFraction(fraction:String, offset:Number = 1):Number
        {
            var result:Number = offset;
            var pow:Number = 0.5;
            var n:int = fraction.length;
            for (var i:int = 0; i < n; i++) 
            {
                result += (fraction.charAt(i) == "1")?pow:0;
                pow *= 0.5;
            }
            return result;
        }
        
        /**
         * 十進数を二進数にして返します。
         * @param    decimal
         * @param    is64Bit
         * @return
         */
        static public function binaryFromDecimal(decimal:Number, is64Bit:Boolean = false):String {
            var sign:String = (decimal < 0)?"1":"0";
            decimal = Math.abs(decimal);
            var exponentLength:int = is64Bit?11:8;
            var fractionLength:int = (is64Bit?64:32) - exponentLength - 1;
            //trace(sign, exponentLength, fractionLength);
            var intNum:int = int(decimal);
            var decNum:Number = decimal - intNum;
            
            var intBi:String = binaryFromInt(intNum);
            var dicBi:String = "";
            var pos:int = 0;
            var count:int = 0;
            
            while (decNum != 0 && pos < fractionLength*2 && count <= fractionLength+intBi.length+1) 
            {
                decNum *= 2;
                dicBi += int(decNum).toString();
                decNum = decNum - int(decNum);
                if(parseInt(dicBi) > 0){
                    count ++;
                }
                pos ++;
            }
            //trace(dicBi,dicBi.length);
            var shiftKeta:int;
            if(intBi == "0"){
                shiftKeta = -(dicBi.length - parseInt(dicBi, 2).toString(2).length + 1);
                dicBi = dicBi.substr( -shiftKeta);
            }else {
                shiftKeta = intBi.length - 1;
                dicBi = intBi.substr(1) + dicBi;
            }
            
            intBi = intBi.substr(0, 1);
            var b:int = Math.pow(2, exponentLength) * 0.5 - 1;
            var exponent:String = (get0String(exponentLength) + binaryFromInt(b + shiftKeta)).substr( -exponentLength);
            return rounding(sign, exponent, dicBi, fractionLength);
        }
        
        /**
         * 端数処理
         * http://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86
         * @param    dicBi
         * @param    fractionLength
         * @return
         */
        static private function rounding(sign:String, exponent:String, dicBi:String, fractionLength:int):String {
            var body:String = dicBi.substr(0, fractionLength);
            var tail:String = dicBi.substr(fractionLength);
            
            trace(body, tail);
            var fraction:String = (dicBi + get0String(fractionLength)).substr(0, fractionLength);
            var result:String = sign + exponent + fraction;
            
            if (tail.substr(0,2) == "10" || tail.substr(0,2) == "11") {
                trace("繰り上がる!");
                var zeroPoint:int = result.lastIndexOf("0");
                result = result.substring(0, zeroPoint) + "1" + get0String(result.substring(zeroPoint + 1).length);
            }else {
                // tailが無い、00、01、1の場合
                trace("繰り上がり無し");
            }
            
            sign = result.substr(0, sign.length);
            exponent = result.substr(sign.length, exponent.length);
            fraction = result.substr(sign.length + exponent.length, fraction.length);
            
            return sign + exponent + fraction;
        }
        /**
         * 指定の長さの0の文字列を返します。
         * @param    num
         * @return
         */
        static private function get0String(num:int):String {
            var result:String = "";
            for (var i:int = 0; i < num; i++) 
            {
                result += "0";
            }
            return result;
        }
        /**
         * intを二進数にして返します。
         * @param    num
         * @return
         */
        static private function binaryFromInt(num:int):String {
            if (num == 0) {
                return "0";
            }
            var result:String = "";
            while (num > 0) 
            {
                result = String(num % 2) + result;
                num = num * 0.5;
            }
            return result;
        }
    }