Delphi Tips
>> Index
● 07/24 集合型変数の内部構造が知りたい/数値として処理したい。
● 07/10 浮動小数点数を整数に丸めるときの注意
● 09/19 Sqr()の結果が負になる!?
● 09/05 数値の2進数への変換で効率的な方法
● 02/11 和暦を西暦に直したい
● 02/08 time_t を TDateTime に変換する
最終更新: 7791 日前
0277 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/28 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/07/24 西坂良幸 編集
集合型変数の内部構造が知りたい/数値として処理したい。
集合型の変数は、フラッグの集まりです。
例えば、
type
TDbit = 0..25;
TDbits = set of TDbit;
と定義し、
Var D:TDbit;
D := [1,3,4,6]; の場合
内部的には,00000000 00000000 00000000 01011010B
とビット表現されています ~~~~~~~~~
したがって、Integer(D)とキャスト可能で、この値は90です
集合で使うバイト数は次の式で計算します。
ByteSize = (Max div 8) - (Min div 8) + 1
※ 最大255要素ですから、最大32バイト
Min と Max は集合の基本型の下限と上限です。
特定の要素 E が何バイト目かは次の式でわかります。
ByteNumber = (E div 8) - (Min div 8)
そのバイト内で何ビット目かは次の式でわかります。
BitNumber = E mod 8
※ E は要素の順序値を表します。
GetLogicalDrives関数は、 現在利用可能なディスクドライブを表すビットマスクを返しますので、これで、試してみましょう。返り値のビット位置0 (最下位ビット) はドライブAに対応します。
バイトサイズ → (25 div 8) - (0 div 8) + 1 = 4
で、4バイト、DWord型やLongint型が使えます。
TDBitsの最小値は → 0
0 mod 8 → 0 ですから、そのまま対応できます
procedure TForm1.Button1Click(Sender: TObject);
var
d: TDbit;
drvs: TDbits;
DName:string;
begin
ListBox1.Items.Clear;
// マシンのドライブをえる
DWORD(drvs) := GetLogicalDrives;
for d := Low(TDbit) to High(TDbit) do
begin
if d in drvs then
begin
DName := Chr(Integer('A') + d) + ':\';
// ドライブタイプをえる
case GetDriveType(PChar(Char(Ord('A')+d)+':\')) of
DRIVE_REMOVABLE: DName := DName + 'リマーブルデスク';
DRIVE_FIXED: DName := DName + 'ハードデスク';
DRIVE_REMOTE: DName := DName + 'ネットワークデスク';
DRIVE_CDROM: DName := DName + 'CDROMデスク';
DRIVE_RAMDISK: DName := DName + 'ラムデスク';
end;
ListBox1.Items.Add(DName);
end;
end;
end;
次に、
Type
TDrive = 'A'..'Z';
TDrives = Set of TDrive; を使えばどうでしょうか。
TDrivesの最小値は → 'A' 順序値 65
65 mod 8 → 1 ですから、
返り値の0ビットは、TDravesの1ビット目に対応します。
procedure TForm1.Button1Click(Sender: TObject);
var
d: TDrive;
drvs: TDrives;
DName:string;
begin
ListBox1.Items.Clear;
// マシンのドライブをえる−−左に1ビットシフトする
DWORD(drvs) := GetLogicalDrives shl 1;
for d := Low(TDrive) to High(TDrive) do
begin
if d in drvs then
begin
DName := Char(d) + ':\';
// ドライブタイプをえる
case GetDriveType(PChar(d +':\')) of
DRIVE_REMOVABLE: DName := DName + 'リマーブルデスク';
DRIVE_FIXED: DName := DName + 'ハードデスク';
DRIVE_REMOTE: DName := DName + 'ネットワークデスク';
DRIVE_CDROM: DName := DName + 'CDROMデスク';
DRIVE_RAMDISK: DName := DName + 'ラムデスク';
end;
ListBox1.Items.Add(DName);
end;
end;
end;
集合型を内部表現で使うのは、注意が必要です。
なるべく集合型として使いましょう。
なお、集合型の演算子として +,*,-,IN が定義されていますので、
not は、 全定義 - 値
xor は、 和集合 - 積集合で代用できます。
要素数は、4バイトまでの集合なら以下のようなぐあいですか?
function GetElements(Drvs: TDrives):DWORD;
var
i:TDrive;
begin
result := 0;
for i := Low(TDrive) to High(TDrive) do
begin
inc(result,DWORD(Drvs) and 1);
DWORD(Drvs) := DWORD(drvs) shr 1;
end;
end;
*リマーブルデスクって・・・(ぷ
参照: [Delphi-ML:9105] [Delphi-ML:42690] <PASCAL>
0073 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/07/10 yamamoto 編集
浮動小数点数を整数に丸めるときの注意
Delphiには四捨五入用の関数として Round() がありますが、この関数は「Banker's Rounding」(銀行家の丸め)をするため、普通の四捨五入とは結果が異なることがあるため注意が必要です。
具体的には、引数がちょうど ???.5 となるとき、通常の四捨五入では答は 1 繰り上げた数になりますが、Round() では結果の一の位が偶数になるように切り上げ/切り捨てが行われます。(例: 0.5 は 0,1.5 は 2 になる)[Delphi-ML:4727][Delphi-ML:12693]から始まるスレッドも参考になります。
これに対して Trunc() はいつも小数部分を切り捨てて返しますので、普通の四捨五入をするには、
function Roundoff(X: Extended): Longint;
begin
if x >= 0 then Result := Trunc(x + 0.5)
else Result := Trunc(x - 0.5);
end;
のような関数を作って使用します。
Professional Edition 以上では、Math ライブラリに Floor()/Ceil() の関数もあります。
Int() 関数は Trunc() と同じ答を Extended 型で返します。
参照: [Delphi-ML:4727] [Delphi-ML:12693] [Delphi-ML:20194] [Delphi-ML:20205] <PASCAL>
0024 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/19 西坂良幸 編集
Sqr()の結果が負になる!?
Sqr()関数は引数にIntegerを渡すと結果にIntegerが返ってきます。
ヘルプで
function Sqr(X: Real): Real;
と書いてあるにもかかわらずです。
Sqr()関数は引数が整数型だと結果は整数型で返り、
引数が実数型だと結果は実数型で返ります。
これが仕様です。
なお、整数型を渡した場合、オーバーフローのチェックは行われません。そのため渡す値によっては
sqr(x)+sqr(y)
の結果が負になる場合があり、これを実行時に検出することはできません。自乗した場合の結果が Integer の制限を越えることが予想できる場合には、引数を実数型にキャストして渡す必要があります。
参照: [Delphi-ML:7250] <バグ> <PASCAL>
0225 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/05 藤中理香 編集
数値の2進数への変換で効率的な方法
ML[Delphi-ML:32617]〜[Delphi-ML:32720]のスレッドにいろいろな変換関数例が示されています。
ベンチワークしたわけではありませんが、効率ということでは、中川さんのコードがお勧めでは?
「10進から2進数への変換に標準関数はありませんが、デルファイではTBitsという
似て非なるクラスがあります。(定義はclassesの中)
そこを見てみますと、インラインアセンブラが使われています。ということで、題材的に
インラインアセンブラが向いているのではないかと思い作ってみました。」[Delphi-ML:32720]中川
以下、中川さんの例示コードをIntToHex型パラメータの関数にしたものです。(4桁ごとのスペースはありません)
function IntToBin(Int: LongInt; Digits: Integer = 32): string;
// 変換の関数
procedure ToBin(Int: LongInt; x: pointer);
asm
push ebx; // ebxは使用不可なので保存
mov edx,x; // 文字列の先頭アドレス
mov eax,int; // 変換する整数
mov ecx,32; // 32回のループのセット
@MAWASU:
mov bl,'0'; // とりあえず文字「0」とする
sal eax,1; // 整数を左シフトして、1ビットを取り出す
jnc @TOBU; // ビットが0ならそのまま
inc bx; // ビットが1ならば文字を「1」にする
@TOBU:
mov [edx],bl; // 文字を書き込む
inc edx; // 文字列のポインタを進める。
loop @MAWASU; // これを繰り返す
mov bl,0; // 文字列の最後は空文字
mov [edx],bl; // 最後の空文字の書き込み
pop ebx; // ebxを呼び戻す
end;
var
Buff: array[0..63] of Char;
begin
if (Digits> 1) and (Digits <= 32) then
begin
ToBin(Int, @Buff);
result := StrPas(Buff + 32 - Digits);
exit;
end;
raise EConvertError.Create('第二パラメータが範囲外です。');
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := IntToBin(16,8);
Edit2.Text := IntToBin(-1);
Edit3.text := IntToBin(Integer($F0F0F0F0));
end;
参照: [Delphi-ML:32720] <PASCAL>
0151 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
和暦を西暦に直したい
こんなのはどうでしょう?
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key<>#13 then
Exit;
Key:= #0;
with Edit1 do
try
Text:= FormatDateTime('yyyy/m/d',VarToDateTime(Text));
SelectAll;
except
on EVariantError do begin
SelectAll;
raise Exception.Create('入力が不正です');
end;
end;
end;
98/8/21 h10/8/21 s24-10-2 m1/1/1 8/21 8/21/98 98/5 1996-8/21
等 ほとんど何でもありです。半角、全角関係なしです。
参照: [Delphi-ML:25313] [Delphi-ML:25315] <文字列> <日時> <PASCAL>
0088 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
time_t を TDateTime に変換する
time_t は、 int (4バイト)で、GMT の 1970年1月1日0時0分0秒からの経過時間を秒単位で表したもので、どうやら2038年1月19日3時14分07秒で破綻するようです。
function time_tToDateTime(vtime_t : Integer) : TDateTime;
const
cDeltaDate : Integer = 25569; // 1970/01/01 の TDateTime値
cSecPerMint : Integer = 60;
cSecPerHour : Integer = (60*60);
cSecPerDay : Integer = (60*60*24);
var
hh, mm, ss : Word;
n : Integer;
begin
vtime_t := vtime_t - cSecPerHour * 9; // JST=+9
n := vtime_t mod cSecPerDay;
hh := n div cSecPerHour;
n := n mod cSecPerHour;
mm := n div cSecPerMint;
ss := n mod cSecPerMint;
Result := (vtime_t div cSecPerDay) +
cDeltaDate + EncodeTime(hh, mm, ss, 0);
end;
参照: [Delphi-ML:18520] <日時> <PASCAL>
[新規作成] [最新の情報に更新]
How To
Lounge
KeyWords
Osamu Takeuchi osamu@big.or.jp
Tips
Delphi
Home