LoginSignup
80
36

More than 3 years have passed since last update.

ナウい言語は暗黙の整数拡張を行わないか?

Last updated at Posted at 2019-10-27

はじめに

@akif999 さんの書かれた記事『C から Go へコードを移植してハマった話 (そして言語仕様へ)』の中で

「あ、C のこの a << 8 は、int に拡張されるんやった」
「C のプログラムがうまくいくのは、整数の演算は暗黙に int へ拡張されるからや…」

「でも、ナウい Go にはそんな暗黙の動作なんてないんや…」

main.c
    c |= (unsigned short)(a << 8);
    c |= (unsigned short)(b << 0);

そうです。
C 言語は、言語が誕生した時代背景や、
言語処理系が CPU へ依存した規格となっていることのせいで、
「未定義動作」や、暗黙の型キャスト、整数の拡張などを頻繁に行います。

との記述があり、「へえ、いまどきのナウい言語は C と違って暗黙の整数拡張を行わないのか」と思ったので検証してみることにしました。

方法

静的型付けを行い、サイズの異なる整数型を持ち、そこそこ使われてる雰囲気の言語/処理系に元記事のプログラムを移植して実行し確認してみました。本来的には言語仕様の書かれたドキュメントを参照すべきですが、面倒なので今回は行っておりません。

整数拡張を行う言語

Java

class Hogera
{
    public static void main(String[] args)
    {
        byte  a = 0x12;
        byte  b = 0x34;
        short c = 0x0000;

        c |= a << 8;
        c |= b << 0;
        System.out.println(String.format("c is 0x%x", c));
    }
}

Wandbox で実行

  • Cと変わらん

D

import std.stdio;

void main()
{
    ubyte  a = 0x12;
    ubyte  b = 0x34;
    ushort c = 0x0000;

    c |= a << 8;
    c |= b << 0;
    writefln("c is 0x%x", c); 
}

Wandbox で実行

  • Cと変わらん

C#

using System;

public class Hogera
{
    public static void Main()
    {
        byte   a = 0x12;
        byte   b = 0x34;
        ushort c = 0x0000;

        c |= (ushort)(a << 8);
        c |= (ushort)(b << 0);
        Console.WriteLine("c is 0x" + c.ToString("x"));
    }
}

Wandbox で実行

  • 暗黙の整数拡張は行われる
  • サイズの異なる整数型のオブジェクトの代入は型変換が必要

Free Pascal

program Hogera(output);

uses sysutils;

var
  a: byte = $12;
  b: byte = $34;
  c: word = $0000;

begin
    c := c or a shl 8;
    c := c or b shl 0;
    writeln('c is $', format('%x', [c]))
end.

Wandbox で実行

  • Cと変わらん

Scala

object Hogera {
  def main(args: Array[String]): Unit = {
    var a: Byte  = 0x12
    var b: Byte  = 0x34
    var c: Short = 0x0000

    c = (c | a << 8).asInstanceOf[Short]
    c = (c | b << 0).asInstanceOf[Short]
    println("c is 0x" + c.toHexString)
  }
}

Wandbox で実行

  • 暗黙の整数拡張は行われる
  • 異なる整数型のオブジェクトの代入は型変換が必要

Groovy

Byte  a = 0x12
Byte  b = 0x34
Short c = 0x0000

c |= a << 8
c |= b << 0
println "c is 0x" + Integer.toHexString(c)

Wandbox で実行

  • Cと変わらん

Perl6 改め Raku

#!/usr/bin/perl6
my uint8  $a = 0x12;
my uint8  $b = 0x34;
my uint16 $c = 0x0000;

$c = $c +| $a +< 8;
$c = $c +| $b +< 0;
say $c.fmt('$c is 0x%x');

Wandbox で実行

整数拡張を行わない言語

Swift

var a:UInt8  = 0x12
var b:UInt8  = 0x34
var c:UInt16 = 0x0000

c |= UInt16(a) << 8
c |= UInt16(b) << 0
print("c is 0x" + String(c, radix:16))

Wandbox で実行

  • 暗黙の整数拡張を行わないので型変換が必要

Rust

fn main() {
    let mut a:u8  = 0x12;
    let mut b:u8  = 0x34;
    let mut c:u16 = 0x0000;

    c |= (a as u16) << 8;
    c |= (b as u16) << 0;
    println!("c is 0x{:x}", c);
}

Wandbox で実行

  • 暗黙の整数拡張を行わないので型変換が必要
  • mut を付けて宣言されたオブジェクトに代入がされないと警告が出るので驚いた

Nim

import strutils

var
  a: uint8  = 0x12
  b: uint8  = 0x34
  c: uint16 = 0x0000

c = c or uint16(a) shl 8
c = c or uint16(b) shl 0
echo "c is 0x", toHex(c)

Wandbox で実行

  • 暗黙の整数拡張を行わないので型変換が必要

Crystal

a = 0x12u8
b = 0x34u8
c = 0x0000u16

c = c | a.to_u16 << 8
c = c | b.to_u16 << 0
puts "c is 0x" + c.to_s(16)

Wandbox で実行

  • 暗黙の整数拡張を行わないので型変換が必要

Kotlin

import java.util.*

fun main(args: Array<String>) {
    var a: Byte  = 0x12
    var b: Byte  = 0x34
    var c: Short = 0x0000

    c = (c.toInt() or a.toInt() shl 8).toShort()
    c = (c.toInt() or b.toInt() shl 0).toShort()
    println("c is 0x" + c.toString(16))
}

ideone で実行

  • 暗黙の整数拡張を行わないので型変換が必要
  • Shortではシフト演算もビット和も許されないようなので一旦Intに変換した
  • 異なる整数型のオブジェクトの代入は型変換が必要

Ada

with Ada.Text_IO, Interfaces;
use  Ada.Text_IO, Interfaces;

procedure test is
   package Unsigned_16_IO is new Ada.Text_Io.Modular_IO(Unsigned_16);
   a: Unsigned_8  := 16#12#;
   b: Unsigned_8  := 16#34#;
   c: Unsigned_16 := 16#0000#;

begin
   c := c or Shift_Left(Interfaces.Unsigned_16(a), 8);
   c := c or Shift_Left(Interfaces.Unsigned_16(b), 0);
   Put("c is ");
   Unsigned_16_IO.Put(Item => c, Base => 16);
   New_Line;
end test;

ideone で実行

  • 暗黙の整数拡張を行わないので型変換が必要
  • 異なる整数型のオブジェクトの代入や演算は型変換が必要
  • 古いデザインにもかかわらず安全性を重視してデザインされた言語であることがわかる
  • 書式は少々古めかしい印象
  • 16進数の書き方が特に煩い

Visual Basic .NET

Imports System

Public Class Hogera
    Public Shared Sub Main()
        Dim a As Byte   = &H12
        Dim b As Byte   = &H34
        Dim c As UShort = &H0000

        c = c Or CType(a, UShort) << 8
        c = c Or CType(b, UShort) << 0
        System.Console.WriteLine("c is &H" + Hex$(c))
    End Sub
End Class

ideone で実行

  • 異なる整数型のオブジェクトの代入や演算に型変換は不要
  • 暗黙の整数拡張を行わず、Byte 型の変数のシフト幅は Byte 型のデータ幅である 8 の剰余が適用されるという誰得な仕様の為に型変換が必要
  • 一貫したポリシーがあるわけでもなく醜く拡張され続けた言語デザインにセンスを感じない

Fortran

      PROGRAM HOGERA
      INTEGER(1) A
      INTEGER(1) B
      INTEGER(2) C
      DATA A,B,C/Z'12',Z'34',Z'0000'/

      C=INT(IOR(INT(C),ISHFT(INT(A),8)),2)
      C=INT(IOR(INT(C),ISHFT(INT(B),0)),2)
      WRITE (*,'("C IS Z''",Z4,"''")') C
      STOP
      END

ideone で実行

  • 暗黙の整数拡張を行わないので型変換が必要
  • シフト演算やビット和は小さい整数には許されないようなので一旦INTEGERに変換した

まとめ

以上の言語と CGo を登場年代順で表にまとめたものが以下です。

言語名 登場年 暗黙の整数拡張
Fortran 1957 ×
C 1972
Ada 1980 ×
Java 1995
Free Pascal 1997
C# 2000
D 2001
Visual Basic .NET 2001 ×
Groovy 2003
Scala 2004
Nim 2008 ×
Go 2009 ×
Rust 2010 ×
Kotlin 2011 ×
Crystal 2014 ×
Swift 2014 ×
Raku 2015

(ここ 10年くらいの間に登場した)ナウい言語では暗黙の整数拡張は行わないことは概ね事実のようです。

おわりに

おわりです。

80
36
17

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80
36