プログラミング C# - 翔ソフトウェア (Sho's)

C#/.NET/ソフトウェア開発など

Build 2016 1日目の発表内容まとめ

マイクロソフトの最大の開発者向けイベント「Build 2016」がサンフランシスコで開催中。

Build 2016

内容は、一部ライブで観ることができる。

1日目に関する記事が、既にあちこちに上がっている。

1日目のキーノートに関する記事も沢山。

1日目の発表内容は以下の通りだ。

1. Windows 10 関連の発表

1.1. BashシェルをWindowsに搭載

Windows 10 で Linux のシェルのバイナリ―をそのまま動作できるようにするとのこと。

マイクロソフトは、最近 Windows 以外の OS との親和性をどんどんと高めており、その一環だろう。

1.2. Windows 10 の大型アップデートについて

2. 「Microsoft Bot Framework」を公開

クラウド上の「マシン ラーニング」機能の応用としての「人工知能」関連だ。

最近マイクロソフトGoogleAmazon などが取り組んでいることで話題になっている。

今回発表されたのは、Bot を作るためのフレームワーク

3. Visual Studio 関連の発表

Visual Studio "15" Preview と Visual Studio 2015 Update 2 RTM が公開された。

Visual Studio 2015 Update 2 は「プログラムと機能」から Visual Studio 2015 の「変更」を選んでインストールするのが簡単だ。

また、MSDN からは Update 2  込みの Visual Studio 2015 がダウンロードできるようになっている。

4. その他

その他、以下のような発表があった。

4.1 Desktop App Converter

Win32/.NETアプリを UWP (*1) アプリに変換するツール

(*1) UWP とは: ユニバーサル Windows プラットフォームの略。Windows 10 を積んだPC や Phone、XBox One、HoloLens、Surface Hub などで動作可能。

4.2 XBox Dev Mode

Visual Studio で XBox One 用に UWP アプリを開発可能に。Visual Studio  からリモートデバッグもできる。

4.3 HoloLens 関連

開発者エディションを提供開始。

4.4 Cortana 関連

様々なアップデートがあったようだ。

4.5 .NET Framework 4.6.2 Preview

「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」でテトリス的なゲームを作った。(Hokuriku ComCamp 2016 - 2月20日)

※ 「[Event] 「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)を開催しました」の続きです。

Hokuriku ComCamp 2016 powered by MVPs」では、北信越の3人の Microsoft MVP (石野 光仁 さん、鈴木 孝明 さん、私) で毎年恒例/大好評の「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」というセッションを行いました。

セッションは、毎回石野 さんが考えてくださっています。 無茶ブリが楽しい素敵なセッションだと思います。

今回の石野さんからのお題は、「30分でテトリス ライクなゲームを作ろう!」というかなりチャレンジングなものでした。

C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」当日の様子

ライブ コーディングは毎回ドキドキものです。

交代で喋り、自分以外が喋っているときにコーディングさせてもらえる、という進行でした。

C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」の様子
「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

私の分の資料

私の作った部分について説明してみます。

私の分の資料を下記で公開しました (slideshareDocs.com にそれぞれ)。

戦略やどのようにインチキ工夫したかを書いてみました。

私のテトリス ライクなゲームの構成

全体の構成は次のようになっています。

FTetris.Model ビューから分離したモデル部分の C#/.NET 版です。他の .NET のプログラムから参照されるクラス ライブラリ (DLL) です。  
FTetris.WPF WPF (Windows Presentation Foundation) で作ったゲームです。C# で書かれています。View と ViewModel からなっており、上記 Model を参照しています。つまり、WPF の標準的な設計方法である MVVM (Model - View - ViewModel) アーキテクチャーで作られています。
FTetris.WPF
FTetris.WinForm Windows Forms 版です。C# で書かれており、GDI+ で描画を行っています。セッション中のライブで主に作った部分です。
FTetris.WinForm
FTetris.Console コンソール アプリケーション版です。C# で書かれています。色がついています。
FTetris.Console
FTetris.Matrix セッションでモデルを説明するためだけに作った、映画マトリックスをイメージした緑色の数字が降ってくる版です。
FTetris.Matrix
FTetris.Win セッションで「インチキ」と称した、Windows Forms のメイン フォームに WebBrowser を貼って下記 TypeScript 版の URL を渡しただけの 1 分で作れる版です。
FTetris.Win
FTetris.TS 上の「インチキ」をやるために作っておいた Web アプリケーション版です。 TypeScript で書かれています。 モデル部分は、上の FTetris.Model から移植しました。 Three.js から WebGL を使っています。 Windows 以外の OS でも動作します (スマートフォンタブレットでも動作可能ですが、操作にはキーボードが必要です)。
FTetris.TS

上記資料にも書きましたが、モデルを (WPFWindows Forms、コンソール等の) ビューから分離しています。

ゲーム本体のロジックをモデルとして書くことで、ビュー側の実装に依存しない形にし、各プラットフォームで使えるようにしました。

FTetris.Model 等の設計

モデル部分の設計は次のようになりました。

このモデルは、先ず C# で実装し、それから TypeScript に移植しましたので、TypeScript 版もほぼ同じ設計です。

モデル部分 FTetris.Model のクラス図
モデル部分 FTetris.Model のクラス図

このモデルを使った例として、WPF 版の設計は次のようになりました。

MVVM (Model - View - ViewModel) になっています。

WPF 版ゲーム FTetris.WPF のクラス図
WPF 版ゲーム FTetris.WPF のクラス図

ソースコード

私の分の全体のソースコードGitHub で公開しています。

TypeScript 版 (HTML/CSS + TypeScript + Three.js + WebGL)

TypeScript で書いた Web アプリケーション版は下のリンクから実際に遊ぶことができます。

くるくる回るようにしたので、通常のものよりだいぶ難しくなっています。

マウスのドラッグやホイールで視点 (カメラ位置) を変えられます。

キー操作

ゲームのキー操作は、どの版も共通で、次のようになっています。

キー動作
左移動
右移動
回転 (時計回り)
回転 (反時計回り)
Space 落下
Enter 最初からプレイ

関連サイト

鈴木さんと石野さんも、ご自身の分のゲームを公開されています。

三者三様で興味深い内容です。

レポート

鈴木さんの記事はこちらです。

彼の作った部分について、アプリケーションやソースコード、戦略や設計なども公開されています。

アプリケーションやソースコードの公開

「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)を開催しました

※ 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」でテトリス的なゲームを作った。(Hokuriku ComCamp 2016 - 2月20日)」に続きます。

石川県金沢市で、毎年恒例のマイクロソフトの技術に関する勉強会 ComCamp を開催しました。

53 名の方に参加していただきました。

Japan ComCamp 2016 powered by MVPs の北陸会場であるこのイベントでは、日本マイクロソフトのカリスマ エバンジェリスト西脇 資哲さんが会場でドローンを飛ばしながら、IT技術との関連を熱く語っていただいたり、恒例の北信越C# 大好き MVP 3名による、C# ライブ コーディングなどおおいに盛り上がりました。

当日の盛り上がりは Twitter などで見ることができます。

セッションは、当日ストリーミング配信されました。後日 Channel 9 で公開される予定です。

開催概要

Hokuriku ComCamp 2016 powered by MVPs
日時 2016/2/20(土) 11:00-18:10 (終了後懇親会)
会場 ITビジネスプラザ武蔵 6F 交流室2
石川県金沢市武蔵町
主催 Hokuriku.NET
福井情報技術者協会[FITEA]
協力 日本マイクロソフト
詳細 http://hokurikucomcamp.connpass.com/event/23628/

スケジュール

セッションスピーカー 
オープニング 小島 富治雄 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: プログラミング C# - 翔ソフトウェア (Sho's)
オープニング
C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

北信越Visual Studio and Development Technologies MVP 3名が C# の魅力について語ります。LT や コーディング ライブ。
石野 光仁
Microsoft MVP for Visual Studio and Development Technologies
コミュニティ: AILight

鈴木 孝明 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: xin9le note

小島 富治雄 氏
「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」
Windows 10 楽しい使い方」

Windows 10 には、今までのWindowsと違う、新しい機能がたくさん盛り込まれました。Windows 10 の新しい機能で楽しくパソコンを使う方法を紹介します。
さくしま たかえ 氏
Microsoft MVP for Windows Experience
ブログ: 世の中は不思議なことだらけ
Microsoft Online Services 検証の館
著書: Windows 10 完全制覇パーフェクト
 
「DataTableを徹底解剖しようぜ!」

DataTableクラスについて、色々といじったり語り合ったりとかどうでしょう?
片桐 継 氏
サイト
 
「エンジニアよ興味を持ち続けろ! ドローンだってITだ」

日本マイクロソフトエバンジェリストなのになぜか興味をもってしまったドローン。
しかしやがて、Microsoftテクノロジーと深い関係に。
興味を持ち続けることの重要性を伝えつつ、ドローンの現状と利活用の世界、さらにはドローンと Microsoft テクノロジーについて解説します。
軽量ドローンは持ち込んで飛ばしますよ~。
西脇 資哲 氏
日本マイクロソフト株式会社 業務執行役員エバンジェリスト
「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏
「いまから始める Windows Server」

Windows Server を触ったことがない方や開発用のサーバーを構築する必要がある方、中小企業などでコンピューターやデータの管理を効果的に行いたい方などに最新の Windows Server を使って紹介をいたします。
澤田 賢也 氏
Microsoft MVP for Cloud and Datacenter Management
ブログ: Windows Server Essentials を中心とした雑記
著書: 現場で役立つ Windows Server 2012 R2 Essentials 構築・運用ガイド
 
「馴染みの .NET Framework で CMS を使ってみよう」 矢後 比呂加 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: miso_soup3 Blog
「馴染みの .NET Framework で CMS を使ってみよう」矢後 比呂加 氏
ライトニングトーク (LT) 金子 雄一氏
リモートデスクトップ関連

むろほしりょうた 氏
「『var禁止』禁止」

山P 氏
「AzureAutomationを使ってみた話」
「『var禁止』禁止」むろほしりょうた 氏
クロージング じゃんけん大会 じゃんけん大会
懇親会   懇親会

関連サイト

資料公開
サンプル公開
参加レポート

『富山合同勉強会 2016 ~ぶりしゃぶ会~』 (2016/01/30)

富山合同勉強会 2016 ~ぶりしゃぶ会~』に参加してきた。

Babylon.js + TypeScript で簡単 3D プログラミング」という題で、ライトニングトーク (LT) を行った。

イベントの概要

富山合同勉強会 2016
開催日 2016/01/30
会場 富山大学 五福キャンパス
詳細 http://toyama-eng.connpass.com/event/24840/

イベントの写真

イベントの様子
懇親会は恒例のブリしゃぶ

当日の様子

当日のスライド (一部)

参加された方が書かれた記事

「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)が開催されます

昨年の様子

この度、石川県金沢市で、毎年恒例のマイクロソフトの技術に関する勉強会 ComCamp を開催します。 是非ご参加ください。

Japan ComCamp 2016 powered by MVPs の北陸会場であるこのイベントでは、日本マイクロソフトの人気エバンジェリスト西脇 資哲さんにドローンとマイクロソフトのテクノロジーについて語っていただきます。
その他、恒例の北信越C# 大好き MVP 3名による、やらせなしライブ コーディングなど盛り沢山です。
ぜひご参加ください! 学生の方も大歓迎です。

前回

※ 前回の様子:

富山合同勉強会 .NET & Java ~ぶりしゃぶで休もう~ - MVP Community Camp 2015 北陸会場 -

開催概要

Hokuriku ComCamp 2016 powered by MVPs
日時 2016/2/20(土) 11:00-18:10
(受付 10:30-、終了後に近くで懇親会)
※ 開始・終了時刻は前後する可能性があります。
会場 ITビジネスプラザ武蔵 6F 交流室2
石川県金沢市武蔵町14番31号
主催 Hokuriku.NET
福井情報技術者協会[FITEA]
協力 日本マイクロソフト
詳細/お申込み http://hokurikucomcamp.connpass.com/event/23628/

スケジュール

※セッションの順番は変更される可能性があります。

時間セッションスピーカー
11:00~11:10 オープニング  
11:10~12:00 C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

北信越Visual Studio and Development Technologies MVP 3名が C# の魅力について語ります。LT や コーディング ライブ。
石野 光仁
Microsoft MVP for Visual Studio and Development Technologies
コミュニティ: AILight

鈴木 孝明 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: xin9le note

小島 富治雄 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: プログラミング C# - 翔ソフトウェア (Sho's)
13:00~13:40 Windows 10 楽しい使い方」

Windows 10には、今までのWindowsと違う、新しい機能がたくさん盛り込まれました。Windows 10の新しい機能で楽しくパソコンを使う方法を紹介します。
さくしま たかえ 氏
Microsoft MVP for Windows Experience
ブログ: 世の中は不思議なことだらけ
Microsoft Online Services 検証の館
著書: Windows 10 完全制覇パーフェクト
13:50~15:00 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」

日本マイクロソフトエバンジェリストなのになぜか興味をもってしまったドローン。
しかしやがて、Microsoftテクノロジーと深い関係に。
興味を持ち続けることの重要性を伝えつつ、ドローンの現状と利活用の世界、さらにはドローンと Microsoft テクノロジーについて解説します。
軽量ドローンは持ち込んで飛ばしますよ~。
西脇 資哲 氏
日本マイクロソフト株式会社 業務執行役員エバンジェリスト
15:10~15:50 「DataTableを徹底解剖しようぜ!」

DataTableクラスについて、色々といじったり語り合ったりとかどうでしょう?
片桐 継 氏
サイト
16:00~16:40 未定 (Windows Server 2016 Essentials 関連) 澤田 賢也 氏
Microsoft MVP for Cloud and Datacenter Management
ブログ: Windows Server Essentials を中心とした雑記
著書: 現場で役立つ Windows Server 2012 R2 Essentials 構築・運用ガイド
16:50~17:30 「馴染みの .NET Framework で CMS を使ってみよう」 矢後 比呂加 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: miso_soup3 Blog
17:40~18:10 ライトニングトーク (LT) 金子 雄一氏
リモートデスクトップ関連
18:30-21:00 懇親会 こちらからお申込みください。

Japan ComCamp 2016 powered by MVPs リンク

Hokuriku.NET のロゴ ステッカーと Microsoft MVP 飴、Microsoft 飴

浮動小数点数型 double と誤差 ~double の内部表現~

IEEE 754 倍精度 浮動小数点形式

C# Advent Calendar 2015 の12月19日の記事。

C 等を使用している場合と異なり、C# では、それがメモリ上でどのような姿をしているのかを意識する機会は少なめだ。

C の場合、型とメモリ上のビットの並びを意識したプログラミングをする機会が多い。ビット演算も比較的良く使われる。 それに比べると、C#Java などの言語だけを使っている場合は、そうした機会は割と少ない。

しかし、C# 等であっても、やはりそれぞれの型がメモリ上でどのようにして値を保持しているかを知っていることが有効な場合がある。 例えば、double の演算では誤差が生じることが多いが、double の内部表現を知ることで、その理由も腑に落ちやすいだろう。

本記事では、敢えて C# の double (System.Double) の内部の表現に焦点を当ててみたい。 後ろの方では、double の内部を見るクラスなども紹介する。

C# 6.0 を使用。

double を足したり引いたりすると誤差が生じる

C# で double や float のような浮動小数点数型を扱う場合には、誤差に注意する必要がある。

次の例では、↑ボタンを押す度に数値に 0.1 を足し、↓ボタンを押す度に数値から 0.1 を引いているのだが、何度も↑ボタンや↓ボタンを押した結果、誤差が生じている。

0.1を足したり引いたりすることで誤差が生じるサンプル (WPF)
0.1を足したり引いたりすることで誤差が生じるサンプル (WPF)

ちなみに、ソース コードは次のようなものだ。

UpDown.xaml.cs
using System.Windows;
using System.Windows.Controls;

namespace 浮動小数点数サンプル.WPF
{
    public partial class UpDown : UserControl
    {
        const double d = 0.1;

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(UpDown), new PropertyMetadata(0.0));

        public double Value {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public UpDown()
        { InitializeComponent(); }

        void OnUpButtonClick  (object sender, RoutedEventArgs e) => Value += d;
        void OnDownButtonClick(object sender, RoutedEventArgs e) => Value -= d;
    }
}
UpDown.xaml
<UserControl x:Class="浮動小数点数サンプル.WPF.UpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:浮動小数点数サンプル.WPF"
             mc:Ignorable="d" 
             d:DesignHeight="80" d:DesignWidth="200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <RepeatButton Content="↑" Click="OnUpButtonClick"/>
        <RepeatButton Grid.Row="1" Content="↓" Click="OnDownButtonClick"/>
        <TextBlock Grid.Column="1" Grid.RowSpan="2" Text="{Binding Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UpDown}}}" HorizontalAlignment="Right" VerticalAlignment="Center" TextWrapping="Wrap"/>
    </Grid>
</UserControl>

問題点を絞るために、もっとシンプルなサンプルで見てみよう。

次の例では、コンソール アプリケーションで、0.1 を 20 回足した後に20回引いてみて、値の変化を表示している。

0.1 を 20 回足した後に20回引くコンソール アプリケーション
using System;

public static class Extension
{
    // 回数繰り返す
    public static void Repeat(this int times, Action action)
    {
        for (var counter = 0; counter < times; counter++)
            action();
    }
}

static class Program
{
    static void WriteLine(double value) => Console.WriteLine($"{value:f16}");

    static void Main()
    {
        // 0.0 から 0.1 ずつ20回増やしていき、0.1 ずつ20回減らしていく
        const int    times = 20;
        const double d     =  0.1;
        double       value =  0.0;
        WriteLine(value);
        times.Repeat(() => WriteLine(value += d));
        times.Repeat(() => WriteLine(value -= d));
    }
}

実行結果は次の通りだ。

最後の方で誤差が生じているのが分かる。

0.1 を 20 回足した後に20回引くコンソール アプリケーションの実行結果

0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.8000000000000000
0.9000000000000000
1.0000000000000000
1.1000000000000000
1.2000000000000000
1.3000000000000000
1.4000000000000000
1.5000000000000000
1.6000000000000000
1.7000000000000000
1.8000000000000000
1.9000000000000000
2.0000000000000000
1.9000000000000000
1.8000000000000000
1.7000000000000000
1.6000000000000000
1.5000000000000000
1.4000000000000000
1.3000000000000000
1.2000000000000000
1.1000000000000000
1.0000000000000000
0.9000000000000000
0.8000000000000000
0.7000000000000000
0.6000000000000000
0.5000000000000000
0.4000000000000000
0.3000000000000000
0.2000000000000000
0.0999999999999998
-0.0000000000000002

どうしてこのような誤差が生じるのだろうか?

情報落ち

「情報落ち」と呼ばれる現象がある。

絶対値が大きな値と小さな値を足したり引いたりした場合、小さい方の数値の末尾の分の情報が失われる、というものだ。

例えば、十進数で 1111111111.11111 と 1.11111111112345 を足す場合を考えてみよう。 double の有効桁は十進数で15桁程度なので、そうした場合を想定してみる。

有効桁が15桁の場合、1111111111.11111 + 1.11111111112345 = 1111111112.22222 となり、足し算の結果が小さい方の数値の末尾の分小さくなっている。 このような演算を繰り返すと、どんどんこの分の誤差が積もっていくことになる。

今回の場合だが、果たして 0.1 を足したり引いたりするだけで、このような誤差が生じるのだろうか。 0.1 の足し算や引き算であれば、有効桁が15桁もあれば誤差は生じないのではなかろうか。

その鍵は、double の内部表現にある。

二進数としての小数

double の内部表現は二進数である。

そこで、二進数としての小数を考えてみよう。

例えば、十進数で 2.5 の場合、これを二進数で表現すると次のようになる。

2.5(10) = 2 + 0.5 = 10(2) + 1×2-1 = 10.1(2)

そして、十進数で 0.1 の場合、二進数で表現すると、次のように循環小数となるのだ。

0.1(10) = 0.0 0011 0011 0011…(2)

double 内部の二進数としての有効桁は、52 + 1桁である。十進数としての 0.1 を入れたつもりでも、実際には循環小数の全ての桁を格納できる訳ではないので、有効桁を超えた分の情報はなくなってしまい、僅かに 0.1 と異なる値が格納されることになる。

また、この場合、有効桁いっぱいまで使って 0.1 に近い値を表現しているので、絶対値がより大きい値との演算で情報落ちが起こることがある。

double の内部表現

では、double の内部表現をもっと詳しくみてみよう。

double は、次のような構造をしている。これは、「IEEE 754 倍精度 浮動小数点形式」と呼ばれる形式だ。

double の内部表現 (IEEE 754 倍精度 浮動小数点形式)
double の内部表現 (IEEE 754 倍精度 浮動小数点形式)

64ビットの中に二進数で、 符号部、指数部、仮数部の三つが格納されており、次のような値を表す。

-1符号部 × 1.仮数部 × 2指数部 ‐ 1023

 

部分ビット数意味補足
符号部 1 ビット 負の数かどうかを表す 1: 負の数、0: 負の数ではない
指数部 11 ビット 指数 1023 足される
仮数 52 ビット 仮数の絶対値 非 0 の場合 1.XXXXX… になるように指数部で調整され、最上位の 1 は省かれる

例えば、2.5(10) の場合だと次のようになる:

2.5(10)
= 10.1(2)
= 10.1(2)×20
= 1.01(2)×21 (1.XXXXX… になるように調整)

そして、それぞれの部分は次のようになる。

部分補足
符号部 0 負の数でない
指数部 10000000000 1(10)+1023(10)=1024(10)=10000000000(2)
仮数 0100000000000000000000000000000000000000000000000000 1.0100000000000000000000000000000000000000000000000000 の先頭の 1. を省いたもの

従って、2.5(10) を double に格納したときの全体の内部表現は、|0|10000000000|0100000000000000000000000000000000000000000000000000| となる。

そして、0.1(10) の場合だと次のようになる:

0.1(10)
= 0.0 0011 0011 0011 …(2) (循環小数となる)
= 1.1 0011 0011 …(2) × 2-4 (1.XXXXX… になるように調整)

それぞれの部分は次のようになる。

部分補足
符号部 0 負の数でない
指数部 ‭01111111011 -4(10)+1023(10)=1019(10)=‭01111111011(2)
仮数 1001100110011001100110011001100110011001100110011010 1.1 0011 0011 …(2) の整数部分の1を省略し、最後の桁の次の桁を「0捨1入」

つまり、0.1(10) を double に格納したときの全体の内部表現は、|0|‭01111111011|1001100110011001100110011001100110011001100110011010| となる。

尚、同じく浮動小数点数型である float に関しても同様だ。こちらは、4 バイト = 32 ビット (符号部 1 ビット、指数部 8 ビット、仮数部 23 ビット) となる。

C# で double の内部の値を見てみよう

最後に、C# で double の内部の値を見るクラスを作ってみよう。

先ず、double をバイト列にしたり、バイト列を十六進数表現に変換したり、十六進数表現を二進数表現に変換したりするメソッド群を用意した。

バイト列に変換するのには、System.BitConverter クラスが使用できる。 また、メモリ上での配置がリトル エンディアンになっている場合は、反転する必要があるが、これも System.BitConverter クラスで判定できる。

十六進数や二進数としての表現は、文字列として得られるようにした。

BinaryUtility.cs
using System;
using System.Linq;
using System.Text;

public static class BinaryUtility
{
    // バイト列をビッグ エンディアンに変換 (メモリ上での配置がリトル エンディアンになっている場合は反転)
    public static byte[] ToBigEndian(this byte[] bytes) =>
        BitConverter.IsLittleEndian ? bytes.Reverse().ToArray() : bytes;

    // double をバイト列に変換 (ビッグ エンディアン)
    public static byte[] ToBytes(this double value) =>
        BitConverter.GetBytes(value).ToBigEndian();

    // バイト列を十六進数に変換
    public static string ToHexadecimalNumber(this byte[] byteArray) =>
        BitConverter.ToString(byteArray).Replace("-", "");

    // 十六進数一桁を二進数に変換
    public static string HexadecimalNumberToBinaryNumber(char hexadecimalNumber) =>
        Convert.ToString(Convert.ToInt32(hexadecimalNumber.ToString(), 16), 2).PadLeft(4, '0');

    // 十六進数を二進数に変換
    public static string HexadecimalNumberToBinaryNumber(string hexadecimalString) =>
        hexadecimalString.Select(character => HexadecimalNumberToBinaryNumber(character))
                         .Aggregate(new StringBuilder(), (stringBuilder, text) => stringBuilder.Append(text))
                         .ToString();

    // 二進数一桁を int に変換
    public static int BinaryNumberToInteger(char binaryNumber) =>
        binaryNumber == '0' ? 0 : 1;
}

次に、これを利用して、double 即ち IEEE 754 倍精度 浮動小数点形式から各部分の値を取り出すクラスを作ってみよう。

IEEE754Double.cs
using System;

// IEEE 754 倍精度 浮動小数点形式 (8バイト)
// 符号部 1bit   マイナス符号があるとき1
// 指数部 11bits 指数 + 2^10−1 (2^10−1 = 1024 - 1 = 1023)
// 仮数部 52bits 整数部分の1を省略
// ※ 仮数部は整数部分が1のみになるように調整
public class IEEE754Double
{
    const int       exponentBias = 1024 - 1; // 指数のバイアス: 2^10−1 = 1024 - 1 = 1023
    readonly double value;                   // double としての値
    readonly string hexadecimalNumber;       // 十六進数
    readonly string binaryNumber;            // ニ進数

    public IEEE754Double(double value)
    {
        this.value        = value;
        hexadecimalNumber = value.ToBytes().ToHexadecimalNumber();
        binaryNumber      = BinaryUtility.HexadecimalNumberToBinaryNumber(HexadecimalNumber);
    }

    // double としての値
    public double Value => value;

    // 十六進数
    public string HexadecimalNumber => hexadecimalNumber;

    // ニ進数
    public string BinaryNumber => binaryNumber;

    // 符号部の二進数 (マイナス符号があるかないか: 符号部 1bit)
    public char SignBinaryNumber => binaryNumber[0];

    // 指数部の二進数 (指数部 11bits)
    public string ExponentBinaryNumber => binaryNumber.Substring(1, 11);

    // 仮数部の二進数 (仮数部 52bits : 整数部分の1を省略した小数部分)
    public string DigitsBinaryNumber => binaryNumber.Substring(1 + 11, 64 - 1 - 11);

    // 符号 (マイナス符号があるかないか: 符号部 1bit から取得)
    public bool Sign => SignBinaryNumber == '1';

    // 指数 (「指数部 11bits: 指数 + 指数のバイアス」から取得)
    public int Exponent => Convert.ToInt32(ExponentBinaryNumber, 2) - exponentBias;

    // 仮数
    public double Digits
    {
        get {
            var wholeDigitsBinaryNumber = "1" + DigitsBinaryNumber; // 二進数
            // 二進数を double に変換
            var result = 0.0;
            for (var index = wholeDigitsBinaryNumber.Length - 1; index >= 0; index--)
                result = result / 2.0 + BinaryUtility.BinaryNumberToInteger(wholeDigitsBinaryNumber[index]);
            return result;
        }
    }

    public override string ToString() => $"{SignString}{Digits} * 2^{Exponent}";

    // 符号文字列 (- または +)
    string SignString => Sign ? "-" : "+";
}

これらを使って、2.5(10) と 0.1(10) の内部の値をみてみよう。

Program.cs
using System;

static class Program
{
    static void WriteLine(double value)
    {
        var i3eDouble = new IEEE754Double(value);
        Console.WriteLine($"{i3eDouble.Value:f16}, {i3eDouble.HexadecimalNumber}, {i3eDouble.BinaryNumber}, {i3eDouble.ToString()}");
    }

    static void Main()
    {
        WriteLine(2.5);
        WriteLine(0.1);
    }
}

実行結果は次のようになる。 先に考察したのと同じ結果だ。


2.5000000000000000, 4004000000000000, 0100000000000100000000000000000000000000000000000000000000000000, +1.25 * 2^1
0.1000000000000000, 3FB999999999999A, 0011111110111001100110011001100110011001100110011001100110011010, +1.6 * 2^-4

試しに、double.MaxValue、double.MinValue、double.PositiveInfinity、double.NegativeInfinity、double.NaN を出してみると次のようになった。


179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000000000, 7FEFFFFFFFFFFFFF, 0111111111101111111111111111111111111111111111111111111111111111, +2 * 2^1023
-179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000000000, FFEFFFFFFFFFFFFF, 1111111111101111111111111111111111111111111111111111111111111111, -2 * 2^1023
∞, 7FF0000000000000, 0111111111110000000000000000000000000000000000000000000000000000, +1 * 2^1024
-∞, FFF0000000000000, 1111111111110000000000000000000000000000000000000000000000000000, -1 * 2^1024
NaN, FFF8000000000000, 1111111111111000000000000000000000000000000000000000000000000000, -1.5 * 2^1024

double.PositiveInfinity、double.NegativeInfinity、double.NaN に関しては最後の部分は無用だが、それぞれの内部表現は確認できた。

では、始めの方で行った、0.1 を足したり引いたりする例について試してみよう。

Program.cs
using System;

public static class Extension
{
    // 回数繰り返す
    public static void Repeat(this int times, Action action)
    {
        for (var counter = 0; counter < times; counter++)
            action();
    }
}

static class Program
{
    static void WriteLine(double value)
    {
        var i3eDouble = new IEEE754Double(value);
        Console.WriteLine($"{i3eDouble.Value:f16}, {i3eDouble.HexadecimalNumber}, {i3eDouble.BinaryNumber}, {i3eDouble.ToString()}");
    }

    static void Main()
    {
        // 0.0 から 0.1 ずつ20回増やしていき、0.1 ずつ20回減らしていく
        const int    times = 20;
        const double d     =  0.1;
        double       value =  0.0;
        WriteLine(value);
        times.Repeat(() => WriteLine(value += d));
        times.Repeat(() => WriteLine(value -= d));
    }
}

実行結果は次のようになった。 二進数の演算で誤差が生じている箇所が確認できるだろうか。


0.0000000000000000, 0000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, +1 * 2^-1023
0.1000000000000000, 3FB999999999999A, 0011111110111001100110011001100110011001100110011001100110011010, +1.6 * 2^-4
0.2000000000000000, 3FC999999999999A, 0011111111001001100110011001100110011001100110011001100110011010, +1.6 * 2^-3
0.3000000000000000, 3FD3333333333334, 0011111111010011001100110011001100110011001100110011001100110100, +1.2 * 2^-2
0.4000000000000000, 3FD999999999999A, 0011111111011001100110011001100110011001100110011001100110011010, +1.6 * 2^-2
0.5000000000000000, 3FE0000000000000, 0011111111100000000000000000000000000000000000000000000000000000, +1 * 2^-1
0.6000000000000000, 3FE3333333333333, 0011111111100011001100110011001100110011001100110011001100110011, +1.2 * 2^-1
0.7000000000000000, 3FE6666666666666, 0011111111100110011001100110011001100110011001100110011001100110, +1.4 * 2^-1
0.8000000000000000, 3FE9999999999999, 0011111111101001100110011001100110011001100110011001100110011001, +1.6 * 2^-1
0.9000000000000000, 3FECCCCCCCCCCCCC, 0011111111101100110011001100110011001100110011001100110011001100, +1.8 * 2^-1
1.0000000000000000, 3FEFFFFFFFFFFFFF, 0011111111101111111111111111111111111111111111111111111111111111, +2 * 2^-1
1.1000000000000000, 3FF1999999999999, 0011111111110001100110011001100110011001100110011001100110011001, +1.1 * 2^0
1.2000000000000000, 3FF3333333333333, 0011111111110011001100110011001100110011001100110011001100110011, +1.2 * 2^0
1.3000000000000000, 3FF4CCCCCCCCCCCD, 0011111111110100110011001100110011001100110011001100110011001101, +1.3 * 2^0
1.4000000000000000, 3FF6666666666667, 0011111111110110011001100110011001100110011001100110011001100111, +1.4 * 2^0
1.5000000000000000, 3FF8000000000001, 0011111111111000000000000000000000000000000000000000000000000001, +1.5 * 2^0
1.6000000000000000, 3FF999999999999B, 0011111111111001100110011001100110011001100110011001100110011011, +1.6 * 2^0
1.7000000000000000, 3FFB333333333335, 0011111111111011001100110011001100110011001100110011001100110101, +1.7 * 2^0
1.8000000000000000, 3FFCCCCCCCCCCCCF, 0011111111111100110011001100110011001100110011001100110011001111, +1.8 * 2^0
1.9000000000000000, 3FFE666666666669, 0011111111111110011001100110011001100110011001100110011001101001, +1.9 * 2^0
2.0000000000000000, 4000000000000001, 0100000000000000000000000000000000000000000000000000000000000001, +1 * 2^1
1.9000000000000000, 3FFE666666666668, 0011111111111110011001100110011001100110011001100110011001101000, +1.9 * 2^0
1.8000000000000000, 3FFCCCCCCCCCCCCE, 0011111111111100110011001100110011001100110011001100110011001110, +1.8 * 2^0
1.7000000000000000, 3FFB333333333334, 0011111111111011001100110011001100110011001100110011001100110100, +1.7 * 2^0
1.6000000000000000, 3FF999999999999A, 0011111111111001100110011001100110011001100110011001100110011010, +1.6 * 2^0
1.5000000000000000, 3FF8000000000000, 0011111111111000000000000000000000000000000000000000000000000000, +1.5 * 2^0
1.4000000000000000, 3FF6666666666666, 0011111111110110011001100110011001100110011001100110011001100110, +1.4 * 2^0
1.3000000000000000, 3FF4CCCCCCCCCCCC, 0011111111110100110011001100110011001100110011001100110011001100, +1.3 * 2^0
1.2000000000000000, 3FF3333333333332, 0011111111110011001100110011001100110011001100110011001100110010, +1.2 * 2^0
1.1000000000000000, 3FF1999999999998, 0011111111110001100110011001100110011001100110011001100110011000, +1.1 * 2^0
1.0000000000000000, 3FEFFFFFFFFFFFFD, 0011111111101111111111111111111111111111111111111111111111111101, +2 * 2^-1
0.9000000000000000, 3FECCCCCCCCCCCCA, 0011111111101100110011001100110011001100110011001100110011001010, +1.8 * 2^-1
0.8000000000000000, 3FE9999999999997, 0011111111101001100110011001100110011001100110011001100110010111, +1.6 * 2^-1
0.7000000000000000, 3FE6666666666664, 0011111111100110011001100110011001100110011001100110011001100100, +1.4 * 2^-1
0.6000000000000000, 3FE3333333333331, 0011111111100011001100110011001100110011001100110011001100110001, +1.2 * 2^-1
0.5000000000000000, 3FDFFFFFFFFFFFFC, 0011111111011111111111111111111111111111111111111111111111111100, +2 * 2^-2
0.4000000000000000, 3FD9999999999996, 0011111111011001100110011001100110011001100110011001100110010110, +1.6 * 2^-2
0.3000000000000000, 3FD3333333333330, 0011111111010011001100110011001100110011001100110011001100110000, +1.2 * 2^-2
0.2000000000000000, 3FC9999999999993, 0011111111001001100110011001100110011001100110011001100110010011, +1.6 * 2^-3
0.0999999999999998, 3FB999999999998C, 0011111110111001100110011001100110011001100110011001100110001100, +1.6 * 2^-4
-0.0000000000000002, BCAC000000000000, 1011110010101100000000000000000000000000000000000000000000000000, -1.75 * 2^-53

まとめ

以上、double の内部表現について、C# を使って説明してみた。

Visual Studio Code の拡張機能「Twitter Client」を試してみる

Visual Studio Code の拡張機能「Twitter Client」を試してみる

Visual Studio / Visual Studio Code Advent Calendar の12月10日の記事。

Visual Studio Code の拡張機能

Microsoft の開発者向けテキスト エディターである Visual Studio Code には、インテリセンスやデバッグ機能などの Visual Studio の素晴らしい機能があり、Windows 以外にも LinuxOS X で使うことができる。オープンソースだ。

豊富な拡張機能も魅力だ。これにより、様々な機能を使うことができる。また、C#JavaScript のみならず、Go、D Language、Haskell 等の沢山のプログラミング言語にも対応できる。

どんな拡張機能があるかは、次のサイトで見ることができる。

Twitter Client 拡張機能

この記事では、Visual Studio Code の拡張機能の一つ、"Twitter Client" を紹介したい。

この拡張機能を使うことによって、テキスト エディターから出ることなく、Twitter のタイムラインを見たり、呟いたりすることができる。

実際にインストールして使ってみよう。

以下で、その手順を示したい。

1. Visual Studio Code のインストール

Visual Studio Code (現在 Version 0.10.3) は、次のサイトからダウンロードしてインストールすることができる。

インストールが完了したら起動してみよう。

Visual Studio Code の起動画面 (Windows の場合)
Visual Studio Code の起動画面 (Windows の場合)
2. Twitter Client 拡張機能のインストール

次に、Visual Studio Code に Twitter Client 拡張機能をインストールする。

詳しい内容は、次のページで見ることができる (英語)。

Visual Studio Code で F1キーを押すと、上にコマンドを入力するテキスト ボックスが現れる。

ここに、"ext install" と入力し、Enter キーを押す。

拡張機能のインストール
拡張機能のインストール

暫く待つと、拡張機能がリストアップされてくる (インターネット接続が必要)。

拡張機能のリストアップ
拡張機能のリストアップ

ここで、"ext install " の後ろに "twitter" と入力する。"Twitter Client" が表示されるので、右側の雲のアイコンをクリックしてインストールする。

"Twitter Client" のインストール
"Twitter Client" のインストール

インストールが終わると、"Restart Now" というボタンが表示される。クリックして Visual Studio Code を再起動しよう。

"Twitter Client" のインストール完了
"Twitter Client" のインストール完了
3. Twitter Developer Account による Twitter App の作成

Twitter のクライアント アプリを作るためには、Twitter Developer Account によって Twitter App の作成を行う必要がある。

その手順は次のページで見ることができる (英語)。

"Twitter Client" には、このステップの為のウィザードが用意されている。見てみよう。

再び F1 キーを押して、コマンドとして "Twi Wizard" と入力する。

"Twitter Client" のウィザードの起動
"Twitter Client" のウィザードの起動

すると、"Twitter Client" のウィザードが起動し、Twitter App をセットアップする手順を教えてくれる。 "Continue" をクリックしよう。

"Twitter Client" のウィザードの開始画面
"Twitter Client" のウィザードの開始画面

すると、"https://apps.twitter.com" で Twitter App を作成するか訊いてくる。 "Continue" をクリックする。

"Twitter Client" のウィザード
"Twitter Client" のウィザード

Web ブラウザーで、"https://apps.twitter.com" が開かれる。 ここで Twitter App の作成を行う。Twitter にログインしよう (Twitter アカウントが必要)。

"Create New App" をクリックする。

Twitter App の作成
Twitter App の作成

Visual Studio Code 側では、ウィザードを進めていこう。

"Twitter Client" のウィザード
"Twitter Client" のウィザード
"Twitter Client" のウィザード
"Twitter Client" のウィザード

すると、Web ブラウザー側では、Twitter App を作成するための必要事項の入力を求められる。 入力しよう。

Twitter App を作成るための必要事項の入力
Twitter App を作成るための必要事項の入力

Twitter App が作成されたら、"Access level" が "Read & write" になっているのを確認しておこう (もし違っていたら、"modify app permissions" から変更しておく)。

Twitter App が作成されたら "Access level" の確認
Twitter App が作成されたら "Access level" の確認

次に、"Keys and Access Tokens" のタブをクリックして切り替える。

ここに表示されている "Consumer Key (API Key)" と "Consumer Secret (API Secret)" は、後で使うのでメモしておく。

そして、下部の "Create my access token" をクリックしよう。

"Keys and Access Tokens"
"Keys and Access Tokens"

"Access token" が表示される。 "Access Token" と "Access Token Secret" をメモっておこう。

"Access token"
"Access token"
4. Twitter Developer Account による Twitter App の作成

メモっておいたキーやトークンは、Visual Studio Code で入力する。

Visual Studio Code で、メニューから "File" - "Preferences" - "User Settings" を選ぶ。

Visual Studio Code で "User Settings" を開く
Visual Studio Code で "User Settings" を開く

"settings.json" というファイルが開く。 右側がユーザー用の設定ファイルだ。 次のように入力する。

"twitter.consumerkey": "xxxx", // Consumer Key (API Key)

"twitter.consumersecret": "xxxx", // Consumer Secret (API Secret)

"twitter.accesstokenkey": "xxxx", // Access Token

"twitter.accesstokensecret": "xxxx" // Access Token Secret
User Settings の編集
User Settings の編集
5. "Twitter Client" を使ってみよう

以上で準備は完了だ。 使ってみよう。

Visual Studio Code の下部に "Twitter" と書かれたボタンが表示されている。

クリックしてみよう。

すると、上の方に Twitter 用のコマンドが表示される。

Twitter のコマンド
Twitter のコマンド

例えば、"Home" をクリックしてみると、自分のタイムラインが表示される筈だ。

Home Timeline
Home Timeline

"User" を選んで自分の呟きだけに切り替え、"Post" を使って呟いてみると次のような感じだ。

自分の呟きを表示して呟いてみる
自分の呟きを表示して呟いてみる

まとめ

今回は、Visual Studio Code の拡張機能の一つ Twitter Client を試した。

Visual Studio Code には、他にも豊富な拡張機能がある。是非試してほしい。